0706A+
T1. 春江潮水连海平
将所有排水口按照 排序。依次插入一个按 排序的大根堆。
每次判断是先弹出一个堆顶的排水口,还是插入下一个排水口,依题意模拟即可。
复杂度
#include<bits/stdc++.h>
using namespace std;
// #define int long long
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
#define fi first
#define se second
const int N = 1e5+5;
const int M = (1<<8)+5;;
const ll inf = 5e18+5;
const int p = 998244353;
int h,n;
pii a[N];
signed main(){
freopen("water.in","r",stdin);
freopen("water.out","w",stdout);
// ios::sync_with_stdio(false); cin.tie(0u); cout.tie(0u);
cin >> h >> n;
bool ok = 0;
for (int i = 1; i <= n; i++){
cin >> a[i].fi >> a[i].se;
if (!a[i].se) ok = 1;
}
if (!ok){
cout << "-1\n";
return 0;
}
sort(a+1,a+n+1);
db ans = 0;
db now = h;
priority_queue<int> q;
for (int i = 1; i <= n; i++){
if (a[i].se >= now) continue;
bool tt = 1;
while (ans < a[i].fi){ // 当前时间
if (q.size()){
if (ans+(db)(now-q.top())/q.size() < a[i].fi){ // 到插入的下一个位置 之前到不了当前时间
ans += (db)(now-q.top())/q.size();
now = q.top();
q.pop();
}else{ // 到插入的下一个位置 超越当前时间
// cout << i << endl;
db t = (db)(a[i].fi-ans)*q.size(); // 到当前时间,能走多远
now -= t;
ans = a[i].fi;
}
}else{
ans = a[i].fi;
}
if (!now){
printf("%.6Lf", ans);
return 0;
}
}
// cout << i << " " << ans << " " << now << '\n';
if (now > a[i].se) q.push(a[i].se);
// while(q.size() && q.top() > now) q.pop();
}
while(q.size()){
ans += (now-q.top())/q.size();
now = q.top();
q.pop();
}
printf("%.6Lf", ans);
return 0;
}
T2. 落月摇情满江树
容易发现想要离开一个子树,只有可能从子树的根,或最左边的叶子(以下称左),或最右边的叶子离开(以下称右)。
一个环想要通过一个子树,只有可能是从根走到左、根走到右、左走到右、当前子树已经满足题目条件,可以用若干个环覆盖这四种情况。
我们可以设计状态 。
当前子树可以用若干个环与一条从根到左的路径表示。
当前子树可以用若干个环与一条从根到右的路径表示。
当前子树可以用若干个环与一条从左到右的路径表示。
当前子树可以用若干个环表示。
依次转移即可,复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e6+5;
int n;
vector<int> v[N];
bool f[N][4];
void dfs(int x){
int sz = v[x].size();
if (!sz){
f[x][1] = f[x][2] = f[x][3] = 1;
return;
}
for (int y:v[x]){
dfs(y);
}
int pl0 = -1, pr0 = sz;
int pl3 = -1, pr3 = sz;
while(pl0 < sz-1 && f[v[x][pl0+1]][0]) pl0++;
while(pl3 < sz-1 && f[v[x][pl3+1]][3]) pl3++;
while(pr0 > 0 && f[v[x][pr0-1]][0]) pr0--;
while(pr3 > 0 && f[v[x][pr3-1]][3]) pr3--;
int lst1 = -1, lst2 = -1;
for (int i = 0; i < sz; i++){
int y = v[x][i];
if (f[y][2] && lst1 != -1 && lst1-1 <= pl3 && i+1 >= pr3) f[x][3] = 1;
if (!f[y][0]) lst1 = -1;
if (f[y][1] && lst1 == -1) lst1 = i;
if (f[y][1] && lst2 != -1 && lst2-1 <= pl0 && i+1 >= pr0) f[x][0] = 1;
if (!f[y][3]) lst2 = -1;
if (f[y][2] && lst2 == -1) lst2 = i;
}
for (int i = 0; i < sz; i++){
int y = v[x][i];
if (f[y][1] && i+1 >= pr0 && i-1 <= pl3) f[x][1] = 1;
if (f[y][2] && i+1 >= pr3 && i-1 <= pl0) f[x][2] = 1;
}
}
int T;
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
ios::sync_with_stdio(false); cin.tie(0u); cout.tie(0u);
cin >> T;
while(T--){
cin >> n;
for (int i = 1; i <= n; i++){
f[i][0] = f[i][1] = f[i][2] = f[i][3] = 0;
v[i].clear();
}
for (int i = 1; i <= n; i++){
int k;
cin >> k;
for (int j = 1; j <= k; j++){
int x;
cin >> x;
v[i].push_back(x);
}
}
dfs(1);
if (f[1][0]) cout << "YES" << '\n';
else cout << "NO" << '\n';
}
return 0;
}
T3. 禁止套娃
题意
求一个序列的所有本质不同子序列的本质不同子序列个数之和 。。
算法一
计算一个序列的本质不同子序列个数的方法如下:
由于会计重,考虑对于每一种子序列,钦定只对它最靠左(贪心)的匹配计数。容易证明存在唯一的这样的子序列到下标的映射。
令 表示末尾选 的本质不同子序列数,则 , 表示上一个与 相等的位置,如果不存在则为 。
或者,令 表示末尾选 的本质不同子序列数,则 。
外层暴枚可做到 ,期望得分 。
算法二
直接讲正解。
设选择的外层子序列下标为集合 ,内层为集合 。为了方便表述,设占位下标 。同样只计贪心匹配的情况,限制如下:
- 中相邻两个数 , 中不存在 的值。
- 中相邻两个数 , 中不存在 的值。
考虑对 dp。 表示目前考虑到 且内外层末尾均选 的答案。如果要从 转移过来,那么就要决定 这部分如何选外层,设选择了集合 ,限制如下;
- 中相邻两个数 , 中不存在 的值。
- 中最大值 , 中不存在 的值。
- 中任意 ,。
一个简洁的处理方法是,对于每一个 ,dp 出 每个 的只需满足 1、3 条件的本质不同子序列个数 ,真正转移时 即可。最后汇总答案可以弄一个必选的占位下标 。
是 2D/0D, 是 1D/1D,时间复杂度 ,期望得分 。
如果您想到了低于平方的解法,请联系 YeahPotato!
#include <bits/stdc++.h>
using namespace std;
#define P 1000000007
int n, a[5005], now[5005], f[5005], g[5005][5005];
int R(int x, int y) { return (x += y) >= P ? x - P : x; }
int main() {
freopen ("nest.in", "r", stdin);
freopen ("nest.out", "w", stdout);
cin >> n;
for (int i=1; i<=n; i++)
scanf ("%d", &a[i]);
for (int i=1; i<=n+1; i++) {
g[i][i] = 1;
for (int j=i-1; j; j--) {
g[i][j] = g[i][j+1];
if (a[j] ^ a[i]) g[i][j] = R(g[i][j], R(g[i][j+1], now[a[j]] ? P - g[i][now[a[j]]+1] : 0));
now[a[j]] = j;
}
for (int j=i-1; j; j--)
now[a[j]] = 0;
}
f[0] = 1;
for (int i=1; i<=n+1; i++) {
for (int j=0; j<i; j++)
f[i] = (f[i] + 1ll * (g[i][j+1] + P - g[now[a[i]]][j+1]) * f[j]) % P;
now[a[i]] = i;
}
cout << f[n+1];
}
T4. presuffix
非常直观的感觉,操作次数不会特别的多,考虑证明上界为 。
令 为给定的数组, 为 的前缀和,令 分别为 的最小值和最大值。
- 若 ,则用操作 令 。
- 不妨令 ,先用操作 操作 ,被操作的所有数 会变成 ,是非负的,因为 ,故最大值的位置仍然在 中,令 为新的最大值出现位置,用操作 操作 ,被操作的所有数 会变成 ,同样非负。
分情况讨论:
若答案为 ,当且仅当所有 。
若答案为 ,不妨要么进行一次 操作,要么存在一个区间满足前/后缀和 ,且区间外的所有数 。而在区间左右两端加上非负数是不劣的,故只要判断前/后缀和数组是否都 。
若答案为 ,直接构造。
若答案为 ,分两种操作类型相同/不同讨论。
若相同,令两次都为 操作,我们需要找到一个区间,满足将这个区间替换成前缀数组后,整个序列的前缀数组均 。令第一步操作的区间为 ,则 的前缀数组需要 ,令 ,是不劣的,故第一次操作一定是操作一个前缀。枚举这个前缀,需要维护:单点修改,求前缀和数组最小值。用线段树维护前缀和数组,单点修改对应了区间加减,查询对应了全局最小值。
若不同,不妨令第一次为 操作,第二次为 操作,考虑分治,令当前区间为 ,中点为 ,令 为原数组, 为前缀和。
对于所有 计算:
- :若第一次操作的右端点为 ,则操作完后 。计算是容易的。
- :若右端点为 ,第一次操作完之后, 的和至少是多少才能保证对于所有 ,有 。 由两部分: 确定, 是容易计算的,预处理原数组中 的后缀最小值,计算 中后缀和的和即可。 有点麻烦,考虑一个位置 ,第一次操作完之后, 中的和 会随着 的增加发生什么变化:令新加入的数为 ,则对于所有 , 会增加 。将所有 看成一条直线 ,计算 可以看做查询凸包上 处的 值。可以用李超树或单调栈维护。
对于所有 计算:
- :原序列中 后缀和之和。
- :第一次操作完之后,若左端点为 ,则 至少为多少才能保证 中所有前缀和非负。同样的考虑将 减少 之后,所有位置 的前缀和 会发生什么变化,这个也可以写成一条直线 ,我们需要找到一个最小的 使得对于所有直线均有 ,即 ,可以看做求直线 和凸包的交点,用单调栈+二分维护,如果不介意多个 的话也可以用李超树维护+二分,也能擦着时限过。
- :原序列中 的和。
一个合法的第一次操作区间 需要满足:
- 的前缀和数组均 ,很好判断。
- 。
三个限制条件分别令 三段满足条件。将所有满足第一条限制的 ,和所有 搞出来,按 或 排序。第三个限制,对于所有 可以看成直线 ,对于所有 可以看做查询 。
复杂度 。
/*
Things to notice:
1. do not calculate useless values
2. do not use similar names
Things to check:
1. submit the correct file
2. time (it is log^2 or log)
3. memory
4. prove your naive thoughts
5. long long
6. corner case like n=0,1,inf or n=m
7. check if there is a mistake in the ds or other tools you use
8. fileio in some oi-contest
9. module on time
10. the number of a same divisor in a math problem
11. multi-information and queries for dp and ds problems
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fi first
#define se second
#define pii pair<long long,long long>
#define mp make_pair
#define pb push_back
const int mod=998244353;
const int inf=0x3f3f3f3f;
const int INF=1e18;
int n,dirt;
struct Line
{
int k,b;
};
struct SegTree
{
int t[800015],tag[800015];
void pushdown(int id)
{
if(tag[id])
{
t[id<<1]+=tag[id],t[id<<1|1]+=tag[id];
tag[id<<1]+=tag[id],tag[id<<1|1]+=tag[id];
tag[id]=0;
}
}
void update_add(int id,int l,int r,int x,int y,int d)
{
if(x<=l&&r<=y)
{
tag[id]+=d,t[id]+=d;
return;
}
int mid=(l+r)>>1;
pushdown(id);
if(x<=mid) update_add(id<<1,l,mid,x,y,d);
if(y>mid) update_add(id<<1|1,mid+1,r,x,y,d);
t[id]=min(t[id<<1],t[id<<1|1]);
}
void update(int i,int x,int y,int tp)
{
if(tp==0)
{
update_add(1,1,n,i,n,-x);
update_add(1,1,n,i,n,y);
}
else
{
update_add(1,1,n,1,i,-x);
update_add(1,1,n,1,i,y);
}
}
}st;
struct Lichao
{
Line p[2000005];
int t[5000005];
int ls[5000005],rs[5000005];
int idx,tid;
void init()
{
idx=0,tid=1;
ls[1]=0,rs[1]=0,t[1]=0;
p[0].k=1,p[0].b=-INF;
}
int add_line(int k,int b)
{
p[++idx].k=k,p[idx].b=b;
return idx;
}
int get_y(int id,int x)
{
return p[id].k*x+p[id].b;
}
void update(int &id,int l,int r,int lid)
{
if(!id) id=++tid,t[id]=0,ls[id]=rs[id]=0;
int mid=(l+1000000+r+1000000)/2-1000000;
if(get_y(lid,mid)>get_y(t[id],mid)) swap(lid,t[id]);
if(get_y(lid,l)>get_y(t[id],l)) update(ls[id],l,mid,lid);
if(get_y(lid,r)>get_y(t[id],r)) update(rs[id],mid+1,r,lid);
}
void insert(int &id,int l,int r,int x,int y,int lid)
{
if(!id) id=++tid,t[id]=0,ls[id]=rs[id]=0;
if(x<=l&&r<=y)
{
update(id,l,r,lid);
return;
}
int mid=(l+1000000+r+1000000)/2-1000000;
if(x<=mid) insert(ls[id],l,mid,x,y,lid);
if(y>mid) insert(rs[id],mid+1,r,x,y,lid);
}
int query(int id,int l,int r,int x)
{
assert(l<=x&&x<=r);
if(!id) return -INF;
int mid=(l+r)/2;
int res=get_y(t[id],x);
if(l==r) return res;
if(x<=mid) res=max(res,query(ls[id],l,mid,x));
else res=max(res,query(rs[id],mid+1,r,x));
return res;
}
}lt;
int A[200005];
int a[200005],b[200005],c[200005],d[200005],e[200005],f[200005],ps[200005],ss[200005],pspm[200005],pssm[200005];
vector <pii > stk;
vector <double > pnt;
double get(pii x,pii y)
{
if(x.fi==y.fi) return (x.se>y.se?-1e18:1e18);
return 1.0*(y.se-x.se)/(x.fi-y.fi);
}
void ins(pii li)
{
if(!stk.size()) stk.pb(li),pnt.pb(-1e18);
else
{
while(stk.size()>1&&get(li,stk.back())<pnt.back()) stk.pop_back(),pnt.pop_back();
pnt.pb(get(li,stk.back())),stk.pb(li);
}
}
double inter(pii l1,pii l2)
{
return 1.0*(l2.se-l1.se)/(l1.fi-l2.fi);
}
int query(pii li)
{
if(!stk.size()) return INF;
pnt.pb(1e18);
int L=0,R=pnt.size()-2,res=0;
while(L<=R)
{
int mid=(L+R)>>1;
double tmp=inter(stk[mid],li);
if(pnt[mid]<=tmp&&tmp<=pnt[mid+1])
{
res=mid;
break;
}
if(tmp>pnt[mid+1]) L=mid+1;
else R=mid-1;
}
pnt.pop_back();
return ceil(inter(stk[res],li));
}
pii divide(int l,int r)
{
if(l>=r) return mp(-1,-1);
int mid=(l+r)>>1;
for(int i=mid+1,s=0;i<=r;i++) s+=A[i],a[i]=s;
lt.init();
int rt=1;
for(int i=mid+1,s=0,sum=0;i<=r;i++)
{
s+=(i-mid)*A[i];
sum+=A[i];
int K=i-mid,B=s;
B-=K*sum;
int lid=lt.add_line(-K,-B);
lt.insert(rt,-n,n,-n,n,lid);
b[i]=lt.query(rt,-n,n,sum);
b[i]=max(b[i],-(pssm[i+1]-ps[i]+s));
}
for(int i=mid,s=0,sum=0;i>=l;i--)
{
sum+=A[i],s+=sum;
d[i]=s;
}
lt.init();
stk.clear(),pnt.clear();
for(int i=mid,sum=0,tag=0;i>=l;i--)
{
sum+=A[i],tag+=sum;
int K=(i+1),B=sum-tag;
ins(mp(-K,-B));
e[i]=query(mp(-i,tag+ps[i-1]));
}
for(int i=mid;i>=l;i--) f[i]=ps[i-1];
vector <array<int,4> > vec;
vec.clear();
for(int i=l;i<=mid;i++) if(pspm[i-1]>=0) vec.pb({e[i],0,i});
for(int i=mid+1;i<=r;i++) vec.pb({a[i],1,i});
sort(vec.begin(),vec.end());
lt.init();
for(int i=0;i<vec.size();i++)
{
int u=vec[i][2];
if(vec[i][1]==0)
{
int K=mid-u+1,B=d[u]+f[u];
int lid=lt.add_line(K,B);
lt.insert(rt,-n,n,-n,n,lid);
}
else
{
int tmp=lt.query(rt,-n,n,a[u]);
if(tmp>=b[u])
{
int v=-1;
for(int j=0;j<i;j++) if(vec[j][1]==0)
{
v=vec[j][2];
int K=mid-v+1,B=d[v]+f[v];
if(K*a[u]+B==tmp) break;
}
assert(v!=-1);
return mp(v,u);
}
}
}
pii tmp=divide(l,mid);
if(tmp.fi!=-1) return tmp;
tmp=divide(mid+1,r);
if(tmp.fi!=-1) return tmp;
return mp(-1,-1);
}
vector <array<int,3> > chk()
{
memset(st.t,0,sizeof(st.t));
memset(st.tag,0,sizeof(st.tag));
for(int i=1;i<=n;i++) st.update(i,0,A[i],0);
for(int i=1,s=0;i<=n;i++)
{
s+=A[i];
st.update(i,A[i],s,0);
if(st.t[1]>=0)
{
vector <array<int,3> > vec;
vec.pb({2,1,i});
vec.pb({2,1,n});
return vec;
}
}
memset(st.t,0,sizeof(st.t));
memset(st.tag,0,sizeof(st.tag));
for(int i=1;i<=n;i++) st.update(i,0,A[i],1);
for(int i=n,s=0;i>=1;i--)
{
s+=A[i];
st.update(i,A[i],s,1);
if(st.t[1]>=0)
{
vector <array<int,3> > vec;
vec.pb({3,i,n});
vec.pb({3,1,n});
return vec;
}
}
memset(pssm,0x3f,sizeof(pssm)),memset(pspm,0x3f,sizeof(pspm));
for(int i=1;i<=n;i++) ps[i]=ps[i-1]+A[i],pspm[i]=pssm[i]=ps[i];
for(int i=1;i<=n;i++) ss[i]=ss[i+1]+A[i];
for(int i=2;i<=n;i++) pspm[i]=min(pspm[i-1],pspm[i]);
for(int i=n-1;i>=1;i--) pssm[i]=min(pssm[i+1],pssm[i]);
pii tmp=divide(1,n);
vector <array<int,3> > vec;
for(int i=0;i<100;i++) vec.pb({-1,-1,-1});
if(tmp.fi==-1) return vec;
vec.clear();
vec.pb({3,tmp.fi,tmp.se}),vec.pb({2,1,n});
return vec;
}
void solve()
{
cin>>n>>dirt;
for(int i=1;i<=n;i++) cin>>A[i];
bool ok=1;
for(int i=1;i<=n;i++) ok&=(A[i]>=0);
if(ok)
{
cout<<"0\n";
return;
}
ok=1;
for(int i=1,s=0;i<=n;i++) s+=A[i],ok&=(s>=0);
if(ok)
{
cout<<"1\n";
cout<<2<<" "<<1<<" "<<n<<"\n";
return;
}
ok=1;
for(int i=n,s=0;i>=1;i--) s+=A[i],ok&=(s>=0);
if(ok)
{
cout<<"1\n";
cout<<3<<" "<<1<<" "<<n<<"\n";
return;
}
for(int i=1;i<=n;i++) A[i]*=-1;
ok=1;
for(int i=1;i<=n;i++) ok&=(A[i]>=0);
if(ok)
{
cout<<"1\n1\n";
return;
}
ok=1;
for(int i=1,s=0;i<=n;i++) s+=A[i],ok&=(s>=0);
if(ok)
{
cout<<"2\n1\n";
cout<<2<<" "<<1<<" "<<n<<"\n";
return;
}
ok=1;
for(int i=n,s=0;i>=1;i--) s+=A[i],ok&=(s>=0);
if(ok)
{
cout<<"2\n1\n";
cout<<3<<" "<<1<<" "<<n<<"\n";
return;
}
for(int i=1;i<=n;i++) A[i]*=-1;
vector <array<int,3> > v1=chk();
reverse(A+1,A+1+n);
vector <array<int,3> > v2=chk();
for(int i=0;i<v2.size();i++)
{
if(v2[i][0]==2) v2[i][0]=3;
else if(v2[i][0]==3) v2[i][0]=2;
v2[i][1]=n-v2[i][1]+1,v2[i][2]=n-v2[i][2]+1;
swap(v2[i][1],v2[i][2]);
}
reverse(A+1,A+1+n);
for(int i=1;i<=n;i++) A[i]*=-1;
vector <array<int,3> > v3=chk();
v3.insert(v3.begin(),{1,-1,-1});
reverse(A+1,A+1+n);
vector <array<int,3> > v4=chk();
v4.insert(v4.begin(),{1,-1,-1});
for(int i=0;i<v4.size();i++)
{
if(v4[i][0]==2) v4[i][0]=3;
else if(v4[i][0]==3) v4[i][0]=2;
v4[i][1]=n-v4[i][1]+1,v4[i][2]=n-v4[i][2]+1;
swap(v4[i][1],v4[i][2]);
}
vector <array<int,3> > ans;
for(int i=0;i<100;i++) ans.pb({-1,-1,-1});
if(v1.size()<ans.size()) ans=v1;
if(v2.size()<ans.size()) ans=v2;
if(v3.size()<ans.size()) ans=v3;
if(v4.size()<ans.size()) ans=v4;
cout<<ans.size()<<"\n";
for(int i=0;i<ans.size();i++)
{
cout<<ans[i][0];
if(ans[i][0]!=1) cout<<" "<<ans[i][1]<<" "<<ans[i][2];
cout<<"\n";
}
}
signed main()
{
freopen("presuffix.in","r",stdin);
freopen("presuffix.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
int _=1;
// cin>>_;
while(_--) solve();
return 0;
}
- 状态
- 已结束
- 规则
- IOI
- 题目
- 4
- 开始于
- 2026-7-6 8:30
- 结束于
- 2026-7-6 12:00
- 持续时间
- 3.5 小时
- 主持人
- 参赛人数
- 15