1 条题解
-
0
#include <cstring> #include <cstdio> const int MX=100005; int n,ind,fir[MX],deep[MX],idx[MX],topf[MX],son[MX],sz[MX],fa[MX]; struct Edge{int to,nxt;}e[MX<<1]; struct Node{int lc,rc,cnt;void cln(){lc=rc=cnt=0;}}; //Node结构体中:lc左颜色,rc右颜色,cnt个数 struct Segtree{ int lazy[MX<<2];Node tree[MX<<2]; void cln(){ //线段树内清空 memset(lazy,0,sizeof lazy); for(int i=0,h=MX<<2;i<h;i++)tree[i].cln(); } void lazy_down(int k,int l,int r){ //懒标记下传 if(!lazy[k])return; int x=lazy[k],mid=(l+r)>>1;lazy[k]=0; lazy[k<<1]=lazy[k<<1|1]=x; tree[k<<1]=(Node){x,x,mid-l}; //为什么是mid-l?因为一共有mid-l+1个点都被染成颜色x,所以考虑间隔有mid-l对 tree[k<<1|1]=(Node){x,x,r-mid-1}; //为什么是r-mid-1?理由同上啦 } void update(int ul,int ur,int nl,int nr,int pos,int num){ if(ul<=nl&&nr<=ur){ tree[pos]=(Node){num,num,nr-nl}; //为什么是nr-nl?理由在lazy_down函数里啦 lazy[pos]=num;return; } lazy_down(pos,nl,nr);int mid=(nl+nr)>>1; if(ul<=mid)update(ul,ur,nl,mid,pos<<1,num); if(mid<ur)update(ul,ur,mid+1,nr,pos<<1|1,num); Node ls=tree[pos<<1],rs=tree[pos<<1|1]; tree[pos]=(Node){ ls.lc,rs.rc,ls.cnt+rs.cnt+(ls.rc==rs.lc) }; //关键↑区间合并,应该比较好懂,关键在于左儿子的右颜色 //和右儿子的左颜色相同时,需要将计数器加一 } Node query(int al,int ar,int nl,int nr,int pos){ if(al<=nl&&nr<=ar)return tree[pos]; lazy_down(pos,nl,nr);int cnt=0,mid=(nl+nr)>>1;Node w1,w2; //cnt负责控制查询情况种类,见下↓ if(al<=mid){cnt++;w1=query(al,ar,nl,mid,pos<<1);} if(mid<ar){cnt+=2;w2=query(al,ar,mid+1,nr,pos<<1|1);} if(cnt==1)return w1;if(cnt==2)return w2; return (Node){ w1.lc,w2.rc,w1.cnt+w2.cnt+(w1.rc==w2.lc) }; //同样是关键的合并,同update函数操作 } }sgtree; void add(int a,int b,int pos){ e[pos]=(Edge){b,fir[a]};fir[a]=pos; } //建边 void swp(int& a,int& b){int t=a;a=b;b=t;} //手写交换swap函数 void all_cln(){ //这个函数是掌管清空的,多组数据一定要小心! //建议:能清的都清了,免得出现什么奇怪的错误 ind=0;sgtree.cln(); memset(sz,0,sizeof sz); memset(fa,0,sizeof fa); memset(idx,0,sizeof idx); memset(fir,0,sizeof fir); memset(son,0,sizeof son); memset(topf,0,sizeof topf); memset(deep,0,sizeof deep); } void dfs1(int x,int f,int d){ deep[x]=d;fa[x]=f;sz[x]=1; for(int i=fir[x];i;i=e[i].nxt){ int h=e[i].to; if(h!=f){ dfs1(h,x,d+1);sz[x]+=sz[h]; if(sz[h]>sz[son[x]])son[x]=h; } } } //树剖常规第一遍dfs void dfs2(int x,int tp){ if(!x)return;topf[x]=tp; idx[x]=++ind;dfs2(son[x],tp); for(int i=fir[x];i;i=e[i].nxt){ int h=e[i].to;if(h!=son[x]&&h!=fa[x])dfs2(h,h); } } //树剖常规第二遍dfs void range_update(int x,int y,int num){ while(topf[x]!=topf[y]){ int tx=topf[x],ty=topf[y]; if(deep[tx]<deep[ty]){swp(x,y);swp(tx,ty);} sgtree.update(idx[tx],idx[x],1,n,1,num); x=fa[tx]; } if(deep[x]<deep[y])swp(x,y); sgtree.update(idx[y],idx[x],1,n,1,num); } //树剖区间修改 int range_query(int x,int y){ //树剖的区间查询,可以说是整个程序的大核心,一定要深刻理解 //建议画图辅助思考,会有一定难度 bool flg=0;//flg表示当前答案应归到哪边,0为ans1,1为ans2 Node h,ans1=(Node){0,0,0},ans2=(Node){0,0,0}; while(topf[x]!=topf[y]){ int tx=topf[x],ty=topf[y]; if(deep[tx]<deep[ty]){flg=!flg;swp(tx,ty);swp(x,y);} //记得同时取反flg h=sgtree.query(idx[tx],idx[x],1,n,1); //以下是最核心部分↓ //请大家一定注意:每次的查询h,一定是左端点在深度小的地方, //右端点在深度大的地方,所以千万不能把左右端点合并错 if(flg) ans2=(Node){ h.lc,ans2.rc,ans2.cnt+h.cnt+(ans2.lc==h.rc) }; //ans2情况 else ans1=(Node){ ans1.lc,h.lc,ans1.cnt+h.cnt+(ans1.rc==h.rc) }; //ans1情况 x=fa[tx]; } if(deep[x]<deep[y]){swp(x,y);flg=!flg;} h=sgtree.query(idx[y],idx[x],1,n,1); if(flg) ans2=(Node){ h.lc,ans2.rc,ans2.cnt+h.cnt+(ans2.lc==h.rc) }; else ans1=(Node){ ans1.lc,h.lc,ans1.cnt+h.cnt+(ans1.rc==h.rc) }; //末处理 return ans1.cnt+ans2.cnt+(ans1.rc==ans2.lc); } int main(){ //freopen("edge.in","r",stdin); //freopen("edge.out","w",stdout); int data;scanf("%d",&data); for(int i=1;i<=data;i++){ all_cln();//别忘了清空!! int m;scanf("%d%d",&n,&m); for(int j=1;j<n;j++){ int a,b;scanf("%d%d",&a,&b); add(a,b,j);add(b,a,j+n-1); } dfs1(1,0,1);dfs2(1,1); for(int j=1;j<=n;j++)sgtree.update(idx[j],idx[j],1,n,1,-idx[j]); //上面这句也很重要,先把每个点起始赋一个互不相同的值, //随便你赋啥 for(int j=1;j<=m;j++){ int opt,a,b; scanf("%d%d%d",&opt,&a,&b); if(opt&1)range_update(a,b,j); //以询问编号j作为“独一无二的颜色” else printf("%d\n",range_query(a,b)); } } //fclose(stdin); //fclose(stdout); return 0; }
- 1
信息
- ID
- 132
- 时间
- 1000ms
- 内存
- 1024MiB
- 难度
- 8
- 标签
- 递交数
- 70
- 已通过
- 10
- 上传者