P10222 [省选联考 2024] 最长待机 题解

· · 题解

前言

省选 100+12+0+100+0+0=212

小丑分数回归 whk 课了。所以没时间写代码,可能会在这两天把代码写完。

upd:写完了。为什么我 3.7 的题解比 3.10 的题解审的要晚啊,急眼了。。。

这题本质上还是个套着 adhoc 皮的 DS。

@[Linkwish](https://www.luogu.com.cn/user/408180) 与我共同 AC 此题!!!! ### 正文 ------------ 我们视一条长度为 $y$ 的全是 $1$ 的链为 $x^y$。 结论: 1. $x^y = x \times (nx^{y-1})$($n$ 为常数),因为此时我们并不能保证最上面的那个 $1$ 中谁输入的 $x$ 更大,所以 $n$ 的影响只是常数级别,可以忽略不计。 1. $x^y+x^{y-1}=(x+1) \times x^{y-1}=x^y$,后面的 $x^{y-1}$ 对式子只有常数上的影响,所以可以忽略不计。 1. $x^{y-1}+x^y>x^y$,因为左式的 $x^{y-1}$ 和右式的 $x^y$ 同时输入,所以可以保证左式的 $x^y$ 比右式大,不能忽略。 考虑 adhoc 部分,我们根据上述结论,对询问的情况分类讨论: 1. $e_{k}=1$,所以此时的答案和 $k$ 所在的子树中,与 $k$ 的 $1$ 最多的链的 $1$ 的数量有关。 1. $e_{k}=0$,此时我们从 $k$ 向下遍历直到遍历到第一个 $1$ 或者叶子结点的 $0$,把它的答案(根据 $e_k = 1$ 部分的答案)从左到右排序,形成一个序列,它的答案就是不能被忽略的数的和,即那些左边没有数字比它大的数的和。 此时就可以 $O(nm)$ 暴力了,可以结合我下面给出的暴力理解。 ```cpp #include<bits/stdc++.h> template<typename G>inline void read(G&x) {G f=1;x=0;char ch=getchar();while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();if(ch=='-') f=-1,ch=getchar();while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();x*=f;} using namespace std; const int MAXN=2e5+5; int n,m; bool c[MAXN]; vector<int> G[MAXN],vec; int bfs(int u,int dep) { int res=dep; for(auto v:G[u]) res=max(res,bfs(v,dep+c[v])); return res; } void dfs(int u) { if(c[u]) { vec.emplace_back(bfs(u,1)); return; } else if(G[u].empty()) vec.emplace_back(0); for(auto v:G[u]) dfs(v); } int op,x; signed main() { read(n),read(m); for(int i=1,k,v;i<=n;++i) { read(c[i]),read(k); for(int j=1;j<=k;++j) { read(v); G[i].emplace_back(v); } } while(m--) { read(op),read(x); if(op==1) c[x]^=1; else { vec.clear(),dfs(x); if(vec.size()==1) printf("%d\n",vec[0]+(vec[0]==0)); else { int las=-1,ans=0,tot=0; for(auto i:vec) { if(i>=las) { if(i==0) ++ans; else ans+=i; ++tot,las=i; } } printf("%d\n",ans+(tot!=1)); } } } return 0; } ``` 发现直接记录每个 $1$ 点的答案是难以修改和查询的,考虑对其进行转化,设 $ans_i$ 是 $i$ 点到根的路径上 $1$ 的数量,发现 $e_k=1$ 的答案就是 $max^r_l\ e_i-e_{k}+1$。$ans_i$ 的维护是平凡的,线段树即可。 如果按上述的方法求 $e_k=0$ 的答案,这是复杂且非常不可做的。我们发现,如果把叶子节点强行设为 $1$,此时从 $k$ 往下走找到的每一个 $e_x = 1$ 的 $ans_x$ 都是相同的,所以我们可以把答案变成在 $[l,r]$ 的 dfs 序上找到若干个最小的 $ans_i$ 把它分割成若干个区间,而得到的序列就是这若干个区间各自的最大值。发现这个区间的分割是可以合并的,所以考虑使用兔队线段树维护。 时间复杂度 $O(m \log^2 n)$,常数一般,应该能过。 upd: 过了,代码如下: ```cpp #include<bits/stdc++.h> template<typename G>inline void read(G&x) {G f=1;x=0;char ch=getchar();while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();if(ch=='-') f=-1,ch=getchar();while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();x*=f;} using namespace std; #define int long long const int MAXN=5e5+5; int n,m;bool c[MAXN]; int L[MAXN],R[MAXN],dep[MAXN],idx[MAXN],cnt; struct node { int k,pre,suf,v,ans,mx,tot; }d[MAXN<<2]; struct Fenwick_Tree { int n,d[MAXN]; void init(int x) {n=x;} void add(int x,int y) {for(int i=x;i<=n;i+=(i&-i)) d[i]+=y;} int query(int x) {int res=0;for(int i=x;i;i-=(i&-i)) res+=d[i];return res;} }BIT; struct Segment_Tree { bool leaf[MAXN<<2]; int lazy[MAXN<<2]; void pushdown(int p) { if(lazy[p]) { modify(p<<1,lazy[p]); modify(p<<1|1,lazy[p]); lazy[p]=0; } } pair<int,int> Merge(node&x,int p) { if(d[p].mx<x.mx) return make_pair(0,0); if(leaf[p]) { x.ans+=d[p].ans,++x.tot; return make_pair(d[p].ans,d[p].ans); }pushdown(p); if(d[p<<1].mx>=x.mx) { auto las=Merge(x,p<<1); if(x.v<d[p<<1|1].v) { if(las.first==las.second) x.ans-=las.first,--x.tot; las.first=max(las.first,d[p<<1|1].mx),las.second=max(las.second,d[p<<1|1].mx); if(las.first==las.second) x.ans+=las.first,++x.tot; return las; } else { if(x.v==d[p<<1].v) { x.ans+=(d[p].ans-d[p<<1].ans),x.tot+=(d[p].tot-d[p<<1].tot); las.first=d[p<<1|1].suf,las.second=max(las.second,d[p<<1|1].mx); } else { if(las.first==las.second) x.ans-=las.first,--x.tot; las.first=max(las.first,d[p<<1|1].pre),las.second=max(las.second,d[p<<1|1].pre); if(las.first==las.second) x.ans+=las.first,++x.tot; x.ans+=d[p].ans,x.tot+=d[p].tot; las.first=d[p<<1|1].suf,las.second=max(las.second,d[p<<1|1].mx); } return las; } } return Merge(x,p<<1|1); } node merge(node x,int p) { if(x.v<d[p].v) { if(x.suf==x.mx) x.ans-=x.suf,--x.tot; x.suf=max(x.suf,d[p].mx),x.mx=max(x.mx,d[p].mx); if(x.suf==x.mx) x.ans+=x.suf,++x.tot; return x; } else if(x.v==d[p].v) { if(x.mx>d[p].pre) { Merge(x,p); x.suf=d[p].suf,x.mx=max(x.mx,d[p].mx); } else { if(x.suf==x.mx) x.ans-=x.suf,--x.tot; x.ans=x.ans+d[p].pre+d[p].ans,x.tot=x.tot+1+d[p].tot; x.suf=d[p].suf,x.mx=max(x.mx,d[p].mx); } return x; } else { if(x.mx>d[p].pre) { x.ans=0,x.tot=0,x.suf=d[p].suf,x.v=d[p].v,x.k=d[p].k; Merge(x,p); x.pre=max(x.mx,d[p].pre),x.mx=max(x.mx,d[p].mx); return x; } else return d[p]; } } void modify(int p,int c) { lazy[p]+=c; d[p].ans+=c*d[p].tot,d[p].v+=c,d[p].mx+=c,d[p].pre+=c,d[p].suf+=c; } void modify(int l,int r,int ql,int qr,int p,int c) { if(l>=ql&&r<=qr) { modify(p,c); return; } int mid=(l+r)>>1;pushdown(p); if(ql<=mid) modify(l,mid,ql,qr,p<<1,c); if(qr>mid) modify(mid+1,r,ql,qr,p<<1|1,c); d[p]=merge(d[p<<1],p<<1|1); } void modify(int l,int r,int k,int p) { if(l==r) { d[p].v=1e9,d[p].mx=-1e9; if(c[idx[l]]) d[p].v=d[p].mx=BIT.query(l); d[p].k=l,d[p].ans=d[p].suf=d[p].mx,d[p].pre=-1e9,d[p].tot=1; return; } int mid=(l+r)>>1;pushdown(p); if(k<=mid) modify(l,mid,k,p<<1); else modify(mid+1,r,k,p<<1|1); d[p]=merge(d[p<<1],p<<1|1); } int query(int l,int r,int ql,int qr,int p) { if(l>=ql&&r<=qr) return d[p].mx; int mid=(l+r)>>1;pushdown(p); if(qr<=mid) return query(l,mid,ql,qr,p<<1); if(ql>mid) return query(mid+1,r,ql,qr,p<<1|1); return max(query(l,mid,ql,qr,p<<1),query(mid+1,r,ql,qr,p<<1|1)); } void query(int l,int r,int ql,int qr,int p,node&res) { if(l>=ql&&r<=qr) { if(l==ql) res=d[p]; else res=merge(res,p); return; } int mid=(l+r)>>1;pushdown(p); if(ql<=mid) query(l,mid,ql,qr,p<<1,res); if(qr>mid) query(mid+1,r,ql,qr,p<<1|1,res); } void build(int l,int r,int p) { if(l==r) { leaf[p]=1; d[p].v=1e9,d[p].mx=-1e9; if(c[idx[l]]) d[p].v=d[p].mx=dep[idx[l]]; d[p].k=l,d[p].ans=d[p].suf=d[p].mx,d[p].pre=-1e9,d[p].tot=1; return; } int mid=(l+r)>>1; build(l,mid,p<<1),build(mid+1,r,p<<1|1); d[p]=merge(d[p<<1],p<<1|1); } }SGT; vector<int> G[MAXN],leaf; void dfs(int u) { dep[u]+=c[u]; L[u]=++cnt,idx[cnt]=u; for(auto v:G[u]) dep[v]=dep[u],dfs(v); R[u]=cnt; if(G[u].empty()) leaf.emplace_back(L[u]); if(c[u]) BIT.add(L[u],1),BIT.add(R[u]+1,-1); } int fa[MAXN],op,x; signed main() { read(n),read(m);BIT.init(n); for(int i=1,j,k;i<=n;++i) { read(c[i]),read(k); while(k--) { read(j);fa[j]=i; G[i].emplace_back(j); } } dfs(1); SGT.build(1,n,1); while(m--) { read(op),read(x); if(op==1) { c[x]^=1; SGT.modify(1,n,L[x],1); if(c[x]) { SGT.modify(1,n,L[x],R[x],1,1); BIT.add(L[x],1),BIT.add(R[x]+1,-1); } else { SGT.modify(1,n,L[x],R[x],1,-1); BIT.add(L[x],-1),BIT.add(R[x]+1,1); } } else { int val=BIT.query(L[fa[x]]); node ans;SGT.query(1,n,L[x],R[x],1,ans); int l=lower_bound(leaf.begin(),leaf.end(),L[x])-leaf.begin(),r=upper_bound(leaf.begin(),leaf.end(),R[x])-leaf.begin()-1; int ll=l,rr=l-1; while(l<=r) { int mid=(l+r)>>1; if(SGT.query(1,n,L[x],leaf[mid],1)<=val) l=mid+1,rr=mid; else r=mid-1; } if(leaf[rr]==R[x]) printf("%lld\n",rr-ll+1+(rr-ll!=0)); else printf("%lld\n",ans.ans+(rr-ll+1)-ans.tot*val+(ans.tot+rr-ll!=0)); } } return 0; } ```