P2486 [SDOI2011]染色
传送门
题目描述
输入输出格式
输入格式:
输出格式:
对于每个询问操作,输出一行答案。
输入输出样例
说明
这是树剖初学者必打的一道题目,很基础,但很难调,根据机房的大佬的说法,
如果树剖错了就重打吧,别改了。。。
思路:
一看就知道是棵树,可以通过树链剖分后维护。颜色就相当于点权,强烈暗示树链剖分。
所以这是很好的一道树链剖分。
所以重点就落在了如何维护区间颜色块数?
我们可以这样考虑:我们考虑小区间与小区间是不是可以合并
假如我们已经知道这左右两段的左右端点和各自的颜色块数,我们可以想象一下对接这两个区间,如果左半段右端点和右半段左端点是一样的话,合起来的区间就会比原来两段区间中颜色块数之和少一个颜色块。
合并的问题解决了,那如何算其中一个区间的颜色块数?
我们和刚刚的方法相似,就是把这个区间分开,然后按照刚刚的办法合并,合并时把同块的减掉,,
如果一直分下去,,是不是和什么数据结构有点类似,,,(其实这是废话树剖除了加线段树其他的真的不会。。。)
树剖后,线段树要记录左端点l,右端点r,左端点的颜色lc,右端点的颜色rc,区间成段更新的标记tag,区间有多少颜色段。区间合并的时候要注意如果左子树的右端和右子树的左端颜色相同那么数量要减一。但是存在一个问题当前剖到的链与上一次的链在相交的边缘可能颜色相同,如果颜色相同答案需要减一。所以统计答案的时候要记录下上一次剖到的链的左端点的颜色,与当前剖到的链右端点的颜色(因为在处理出的线段树中越靠近根的点位置越左),比较这两个颜色,若相同则答案减1。又由于有u和v两个位置在向上走,那么要记录ans1,ans2两个变量来存“上一次的左端点颜色”。有一点需要注意,当top[u]=top[v]的时候,即已经在同一个重链上时,两边端点颜色都要考虑与对应ans比较颜色,相同答案要相应减一。即如果右儿子的最左边颜色和左儿子的最右边颜色相同,那么肯定有中间部分属于同一颜色段。
详见代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 6 using namespace std; 7 8 const int maxn=1e5+10; 9 struct node 10 { 11 int next; 12 int to; 13 int from; 14 }way[maxn<<1]; 15 struct rr 16 { 17 int lc,rc; 18 int l,r,num; 19 int lazy; 20 }tree[maxn<<2]; 21 int head[maxn]; 22 int size[maxn]; 23 int top[maxn]; 24 int n,m; 25 int son[maxn]; 26 char str[maxn]; 27 int pre[maxn]; 28 int LC,RC; 29 int father[maxn]; 30 int summ[maxn]; 31 int deep[maxn]; 32 int tuop[maxn]; 33 int tot; 34 int cnt; 35 36 int add(int x,int y) 37 { 38 way[++tot].next=head[x]; 39 way[tot].to=y; 40 way[tot].from=x; 41 head[x]=tot; 42 } 43 44 void dfs1(int x,int fa) 45 { 46 size[x]=1; 47 for(int i=head[x];i;i=way[i].next) 48 { 49 int to=way[i].to; 50 if(to==fa) 51 continue; 52 deep[to]=deep[x]+1; 53 father[to]=x; 54 dfs1(to,x); 55 size[x]+=size[to]; 56 if(size[to]>size[son[x]]) 57 { 58 son[x]=to; 59 } 60 } 61 } 62 63 void dfs2(int x,int tp) 64 { 65 top[x]=tp; 66 tuop[x]=++cnt; 67 pre[cnt]=x; 68 if(son[x]) 69 { 70 dfs2(son[x],tp); 71 } 72 for(int i=head[x];i;i=way[i].next) 73 { 74 int to=way[i].to; 75 if(to==father[x]||to==son[x]) 76 { 77 continue; 78 } 79 dfs2(to,to); 80 } 81 } 82 83 inline int pushdown(int x) 84 { 85 if(tree[x].lazy) 86 { 87 tree[x<<1].lazy=tree[x<<1|1].lazy=tree[x].lazy; 88 tree[x<<1].num=tree[x<<1|1].num=1; 89 tree[x<<1].lc=tree[x<<1].rc=tree[x<<1|1].lc=tree[x<<1|1].rc=tree[x].lc; 90 tree[x].lazy=0; 91 } 92 } 93 94 95 inline int pushup(int x) 96 { 97 tree[x].lc=tree[x<<1].lc; 98 tree[x].rc=tree[x<<1|1].rc; 99 int ans=tree[x<<1].num+tree[x<<1|1].num; 100 if(tree[x<<1].rc==tree[x<<1|1].lc) 101 { 102 ans--; 103 } 104 tree[x].num=ans; 105 } 106 107 void build(int x,int l,int r) 108 { 109 tree[x].l=l; 110 tree[x].r=r; 111 tree[x].num=0; 112 if(l==r) 113 { 114 return ; 115 } 116 int mid=(l+r)>>1; 117 build(x<<1,l,mid); 118 build(x<<1|1,mid+1,r); 119 } 120 121 void jia(int x,int l,int r,int c) 122 { 123 if(tree[x].l==l&&tree[x].r==r) 124 { 125 tree[x].num=tree[x].lazy=1; 126 tree[x].lc=tree[x].rc=c; 127 return ; 128 } 129 pushdown(x); 130 int mid=(tree[x].l+tree[x].r)>>1; 131 if(r<=mid) 132 jia(x<<1,l,r,c); 133 else 134 if(l>mid) 135 jia(x<<1|1,l,r,c); 136 else 137 { 138 jia(x<<1,l,mid,x); 139 jia(x<<1|1,mid+1,r,c); 140 } 141 pushup(x); 142 } 143 144 int cha(int x,int l,int r,int ls,int rs) 145 { 146 if(tree[x].l==ls) 147 LC=tree[x].lc; 148 if(tree[x].r==rs) 149 RC=tree[x].rc; 150 if(tree[x].l==l&&tree[x].r==r) 151 { 152 return tree[x].num; 153 } 154 pushdown(x); 155 int mid=(tree[x].l+tree[x].r)>>1; 156 if(r<=mid) 157 { 158 return cha(x<<1,l,r,ls,rs); 159 } 160 else 161 if(l>mid) 162 { 163 return cha(x<<1|1,l,r,ls,rs); 164 } 165 else 166 { 167 int ans=cha(x<<1,l,mid,ls,rs)+cha(x<<1|1,mid+1,r,ls,rs); 168 if(tree[x<<1].rc==tree[x<<1|1].lc) 169 { 170 ans--; 171 } 172 return ans; 173 } 174 pushup(x); 175 } 176 int qcha1(int x,int to,int c) 177 { 178 while(top[x]!=top[to]) 179 { 180 if(deep[top[x]]<deep[top[to]]) 181 { 182 swap(x,to); 183 } 184 jia(1,tuop[top[x]],tuop[x],c); 185 x=father[top[x]]; 186 } 187 if(deep[to]<deep[x]) 188 { 189 swap(x,to); 190 } 191 jia(1,tuop[x],tuop[to],c); 192 } 193 194 int qcha2(int x,int to,int c) 195 { 196 int ans=0; 197 int ans1=-1; 198 int ans2=-1; 199 while(top[x]!=top[to]) 200 { 201 if(deep[top[x]]<deep[top[to]]) 202 { 203 swap(x,to); 204 swap(ans1,ans2); 205 } 206 ans+=cha(1,tuop[top[x]],tuop[x],tuop[top[x]],tuop[x]); 207 if(RC==ans1) 208 ans--; 209 ans1=LC; 210 x=father[top[x]]; 211 } 212 if(deep[x]<deep[to]) 213 { 214 swap(x,to); 215 swap(ans1,ans2); 216 } 217 ans+=cha(1,tuop[to],tuop[x],tuop[to],tuop[x]); 218 if(RC==ans1) 219 { 220 ans--; 221 } 222 if(LC=ans2) 223 { 224 ans--; 225 } 226 return ans; 227 } 228 int main() 229 { 230 while(~scanf("%d%d",&n,&m)) 231 { 232 cnt=tot=0; 233 memset(head,-1,sizeof(head)); 234 for(int i=1;i<=n;i++) 235 { 236 scanf("%d",&summ[i]); 237 } 238 for(int i=1;i<n;i++) 239 { 240 int v,o; 241 scanf("%d%d",&v,&o); 242 add(v,o); 243 add(o,v); 244 } 245 //cout<<"11111111111"<<endl; 246 deep[1]=1; 247 father[1]=1; 248 dfs1(1,1); 249 dfs2(1,1); 250 build(1,1,n); 251 for(int i=1;i<=n;i++) 252 { 253 jia(1,tuop[i],tuop[i],summ[i]); 254 } 255 //cout<<"111111111111111111111111111111111111111111111111"<<endl; 256 while(m--) 257 { 258 scanf("%s",str); 259 int u,v; 260 if(str[0]=='C') 261 { 262 int c; 263 cin>>u>>v>>c; 264 qcha1(u,v,c); 265 } 266 else 267 { 268 int u,v; 269 cin>>u>>v; 270 cout<<qcha2(u,v,0); 271 } 272 } 273 } 274 return 0; 275 }