「P7394 「TOCO Round 1」History」题解
1.「P3627 [APIO2009] 抢掠计划(ATM)」题解2.「P3225 [HNOI2012] 矿场搭建」题解3.「P3214 [HNOI2011] 卡农」题解4.「旅游景点 Tourist Attractions」题解5.「ARC_172_A_Chocolate」题解6.「P1967 [NOIP 2013 提高组] 货车运输」题解7.「佳佳的 Fibonacci」题解8.「Fibonacci 前 n 项和」题解9.「P10668 BZOJ2720 [Violet 5] 列队春游」题解
10.「P7394 「TOCO Round 1」History」题解
11.「ABC375」题解12.「P6054 [RC-02] 开门大吉」题解13.「最小割树」学习笔记 & 「P4897 【模板】最小割树(Gomory-Hu Tree)」 题解做法一,bfs 序
双 log 做法,目前最优解,吊打单 log。
bfs 序的一些性质
-
对于一段单调递增的 bfs 序的对应节点的 k 级祖先的 bfs 序是单调不下降的,证明显然。
-
对于节点
,他的 级子孙组成的 bfs 序是连续的一段区间。
对于本题
显然当
那么我们可以利用 bfs 序的这两个性质,二分出该连续区间的左右端点,这个复杂度是
对于操作
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
#define pb push_back
#define lowbit(x) (x&-x)
using namespace std;
const int N=1e5+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,tot,c[N],to[N],bfn[N],pos[N],dep[N],yet[N],ans[N],fa[N][20];
bool a[N]; vector<int>e[N],g[N];
struct aa {int op,x,y;}que[N];
void add(int x) {for(x++;x<=n+1;x+=lowbit(x)) c[x]++;}
void del(int x) {for(x++;x<=n+1;x+=lowbit(x)) c[x]--;}
int ask(int x) {int res=0; for(x++;x;x-=lowbit(x)) res+=c[x]; return res;}
int ask(int l,int r) {return ask(r)-ask(l-1);}
void bfs()
{
queue<int>q; q.push(1);
while(!q.empty())
{
int x=q.front(); q.pop(),dep[pos[bfn[x]=++tot]=x]=dep[fa[x][0]]+1;
for(int i=1;i<=__lg(dep[x])+1;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int y:e[x]) if(!bfn[y]) q.push(y),fa[y][0]=x;
}
}
int find(int x,int k)
{
if(dep[x]-1<k) return 0;
for(int i=__lg(dep[x])+1;i>=0;i--) if((k>>i)&1) x=fa[x][i];
return bfn[x];
}
int solve(int x,int k)
{
int l=1,r=n,pre=0,suf=0;
while(l<=r)
{
int mid=l+r>>1;
if(find(pos[mid],k)>=x) pre=mid,r=mid-1;
else l=mid+1;
}
l=1,r=n;
while(l<=r)
{
int mid=l+r>>1;
if(find(pos[mid],k)<=x) suf=mid,l=mid+1;
else r=mid-1;
}
return ask(pre,suf);
}
void dfs(int i)
{
int op=que[i].op,x=que[i].x,y=que[i].y;
if(op==1) a[x]?del(bfn[x]):add(bfn[x]),a[x]^=1;
else if(op==2)
{
if(y&1) ans[i]=0; else if(!y) ans[i]=a[x];
else if(dep[x]-1<(y>>=1)) ans[i]=0;
else ans[i]=solve(find(x,y),y)-solve(find(x,y-1),y-1);
}
for(int j:g[i]) dfs(j);
if(op==1) a[x]?del(bfn[x]):add(bfn[x]),a[x]^=1;
}
signed main()
{
read(n);
for(int i=1,x,y;i<n;i++) read(x,y),e[x].pb(y),e[y].pb(x);
bfs(); read(m);
for(int i=1,op,x,y;i<=m;i++)
{
read(op,x); if(op==2) read(y);
op!=3?g[i-1].pb(i):g[x].pb(i),que[i]={op,x,y};
}
dfs(0);
for(int i=1;i<=m;i++) if(que[i].op==2) write(ans[i]),puts("");
}
做法二,dfs 序
单 log 做法,但是线段树维护常数较大。
dfs 序的一些性质
对于节点 x,他的子树上所有节点构成的 dfs 序是连续的一段区间。
对于本题
做法一说过的这里不再说,只是如何维护的区别。
给每个深度开一棵 dfs 序的动态开点线段树,那么对于查询操作,即在对应深度的线段树上查询
同样需减去
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
#define pb push_back
using namespace std;
const int N=1e5+10,M=2e6+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,tot,rt[N],ls[M],rs[M],in[N],out[N],dep[N],val[M],ans[N],fa[N][20];
struct aa {int op,x,y;}q[N]; vector<int>e[N],g[N]; bool a[N];
void change(int &p,int l,int r,int x,int d)
{
if(!p) p=++tot;
if(l==r) return val[p]+=d,void();
int mid=l+r>>1;
x<=mid?change(ls[p],l,mid,x,d):change(rs[p],mid+1,r,x,d);
val[p]=val[ls[p]]+val[rs[p]];
}
int ask(int p,int l,int r,int vl,int vr)
{
if(!p) return 0;
if(vl<=l&&vr>=r) return val[p];
int res=0,mid=l+r>>1;
if(vl<=mid) res+=ask(ls[p],l,mid,vl,vr);
if(vr>mid) res+=ask(rs[p],mid+1,r,vl,vr);
return res;
}
int ask(int x,int k) {return ask(rt[k],1,n,in[x],out[x]);}
void dfs(int x,int t)
{
in[x]=++tot,dep[x]=dep[fa[x][0]=t]+1;
for(int i=1;i<=__lg(dep[x]);i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int y:e[x]) if(y!=t) dfs(y,x); out[x]=tot;
}
int find(int x,int k)
{
if(dep[x]-1<k) return 0;
for(int i=__lg(dep[x]);~i;i--) if((k>>i)&1) x=fa[x][i];
return x;
}
void solve(int i)
{
int op=q[i].op,x=q[i].x,y=q[i].y,tmp;
if(op==1) change(rt[dep[x]],1,n,in[x],a[x]?-1:1),a[x]^=1;
if(op==2) if(!(y&1))
{
if(!y) ans[i]=a[x];
else if(fa[tmp=find(x,(y>>1)-1)][0])
ans[i]=ask(fa[tmp][0],dep[x])-ask(tmp,dep[x]);
}
for(int j:g[i]) solve(j);
if(op==1) change(rt[dep[x]],1,n,in[x],a[x]?-1:1),a[x]^=1;
}
signed main()
{
read(n);
for(int i=1,x,y;i<n;i++) read(x,y),e[x].pb(y),e[y].pb(x);
dfs(1,0),tot=0; read(m);
for(int i=1,op,x,y;i<=m;i++)
{
read(op,x); if(op==2) read(y);
op!=3?g[i-1].pb(i):g[x].pb(i),q[i]={op,x,y};
}
solve(0);
for(int i=1;i<=m;i++) if(q[i].op==2) write(ans[i]),puts("");
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具