计蒜客 39272.Tree-树链剖分(点权)+带修改区间异或和 (The 2019 ACM-ICPC China Shannxi Provincial Programming Contest E.) 2019ICPC西安邀请赛现场赛重现赛
Ming and Hong are playing a simple game called nim game. They have nn piles of stones numbered 11 to nn ,the ii-th pile of stones has a_iai stones. There are n - 1n−1 bidirectional roads in total. For any two piles, there is a unique path from one to another. Then they take turns to pick stones, and each time the current player can take arbitrary number of stones from any pile. Of course, the current player should pick at least one stone. Ming always takes the lead. The one who takes the last stone wins this game. Ming and Hong are smart enough so they will make optimal operations every time.
m events will take place. Each event has a type called optopt ( the value of opt is among \{1,2,3 \}{1,2,3} ).
If optopt is 11, modify the numbers of stones of piles on the path from 1 to ss by the following way: change a_iai to a_i|tai∣t. ( s,ts,t will be given).
If optopt is 22 , modify the numbers of stones of piles on the path from 1 to ss by the following way: change a_iai to a_i\&tai&t. (s,ts,t will be given).
If optopt is 33 , they play nim game. Firstly, take the piles on the path from 1 to SS into consideration. At the same time create a new pile with t stones and take it into consideration. Now they have taken + 1+1 piles into consideration. Then they play nim game on these S + 1S+1 piles. You need to figure out if Ming is going to win the game. Amazingly, after playing nim game, the numbers of stones of each pile on the path from 11 to ss will return to the original numbers.
Input
Each test file contains a single test case. In each test file:
The first line contains two integers,nn and mm.
The next line contains nn integers, a_iai.
The next n - 1 lines, each line contains two integers, b, c, means there is a bidirectional road between pile b and pile c.
Each of the following mm lines contains 33 integers, optopt, ss and tt.
If optopt is 11, change all the stone numbers of piles on the path from 11 to ss in the first way (a_i(ai to a_i|t)ai∣t)
If optopt is 22, change all the stone numbers of piles on the path from 11 to ss in the second way (a_i(ai to a_i\&t)ai&t).
If optopt is 33, we query whether Ming will win when given ss and tt as parameters in this round.
It is guaranteed:1 \le n,m \le 10^51≤n,m≤105,
0 \le a_i \le 10^90≤ai≤109,
1 \le opt \le 3, 1 \le s \le 10^5, 0 \le t \le 10^9.1≤opt≤3,1≤s≤105,0≤t≤109.
Output
If optopt is 33, print a line contains one word “YES” or “NO” (without quotes) , representing whether Ming will win the game.
样例输入
6 6 0 1 0 3 2 3 2 1 3 2 4 1 5 3 6 3 1 3 0 2 3 1 1 1 0 3 5 0 2 4 1 3 3 1
样例输出
YES NO
题意就是给你一棵点权树,有三种操作:
1.从根节点到节点u的路径上的所有点的权值&t
2.从根节点到节点u的路径上的所有点的权值|t
3.查询根节点到节点u的路径所有点的异或和是否等于t
3操作是和尼姆博奕联系起来的,分析得出以上操作。
思路:树链剖分给点重新编号然后线段树来维护。因为涉及到位运算,对于每一位建一棵线段树,建最多不超过31棵线段树。
然后每一棵线段树维护当前位上的区间异或和,当前二进制位为1的数量。若区间和为奇数说明这一位的区间异或结果为1,否则为0。
对于修改操作:
-
修改1为区间或操作:对于二进制的第i位,若t的二进制第i位为1,则会将从1到s的路径上的点权的二进制第i位全变为1,若t的二进制第ii位为0,则无影响
-
修改2为区间与操作:对于二进制的第ii位,若t的二进制第ii位为0,则会将从1到s的路径上的点权的二进制第i位全变为0,若t的二进制第ii位为1,则无影响
参考来源:
2019 ACM-ICPC 西安全国邀请赛 E-Tree 树链剖分+线段树
2019 icpc西安邀请赛 E. Tree(树链剖分+带修改的区间异或和)
我写的时候,直接按以前的板子写发现过不了,后来看的人家的题解改的过的。不知道为什么。
代码:
1 //E.树链剖分+线段树 2 //Nim和!= 0时,先手胜利,否则失败 3 #include<iostream> 4 #include<cstdio> 5 #include<cstring> 6 #include<algorithm> 7 #include<bitset> 8 #include<cassert> 9 #include<cctype> 10 #include<cmath> 11 #include<cstdlib> 12 #include<ctime> 13 #include<deque> 14 #include<iomanip> 15 #include<list> 16 #include<map> 17 #include<queue> 18 #include<set> 19 #include<stack> 20 #include<vector> 21 using namespace std; 22 typedef long long ll; 23 24 const double PI=acos(-1.0); 25 const double eps=1e-6; 26 //const ll mod=1e9+7; 27 const int inf=0x3f3f3f3f; 28 const int maxn=1e5+10; 29 const int maxm=100+10; 30 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 31 #define lson l,m,rt<<1 32 #define rson m+1,r,rt<<1|1 33 34 int tree[31][maxn<<2],lazy[31][maxn<<2]; 35 int n,m,r,mod; 36 int head[maxn],tot; 37 38 int son[maxn],tid[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn]; 39 int w[maxn],wt[maxn]; 40 41 struct Edge{ 42 int to,next; 43 }edge[maxn<<1]; 44 45 void add(int u,int v)//链式前向星存边 46 { 47 edge[tot].to=v; 48 edge[tot].next=head[u]; 49 head[u]=tot++; 50 } 51 52 void init()//初始化 53 { 54 memset(head,-1,sizeof(head)); 55 tot=0;cnt=0; 56 } 57 58 //线段树部分 59 void pushup(int tree[],int lazy[],int rt)//上传lazy标记 60 { 61 tree[rt]=tree[rt<<1]+tree[rt<<1|1]; 62 if(lazy[rt<<1]==lazy[rt<<1|1]) lazy[rt]=lazy[rt<<1]; 63 } 64 65 void pushdown(int tree[],int lazy[],int rt,int m)//下放lazy标记 66 { 67 if(lazy[rt]==-1){ 68 return ; 69 } 70 71 lazy[rt<<1]=lazy[rt]; 72 lazy[rt<<1|1]=lazy[rt]; 73 tree[rt<<1]=(m-(m>>1))*lazy[rt]; 74 tree[rt<<1|1]=(m>>1)*lazy[rt]; 75 lazy[rt]=-1; 76 } 77 78 void update(int tree[],int lazy[],int L,int R,int c,int l,int r,int rt)//区间更新 79 { 80 if(L<=l&&r<=R){ 81 lazy[rt]=c; 82 tree[rt]=c*(r-l+1); 83 return ; 84 } 85 86 pushdown(tree,lazy,rt,r-l+1); 87 int m=(l+r)>>1; 88 if(L<=m) update(tree,lazy,L,R,c,lson); 89 if(R> m) update(tree,lazy,L,R,c,rson); 90 pushup(tree,lazy,rt); 91 } 92 93 int query(int tree[],int lazy[],int L,int R,int l,int r,int rt) 94 { 95 if(L<=l&&r<=R){ 96 return tree[rt]; 97 } 98 99 int ret=0; 100 pushdown(tree,lazy,rt,r-l+1); 101 int m=(l+r)>>1; 102 if(L<=m) ret+=query(tree,lazy,L,R,lson); 103 if(R> m) ret+=query(tree,lazy,L,R,rson); 104 return ret; 105 } 106 107 //树链剖分部分 108 void dfs1(int u,int father) 109 { 110 siz[u]=1;//记录每个节点子树大小 111 fa[u]=father;//标记节点的父亲 112 dep[u]=dep[father]+1;//标记深度 113 for(int i=head[u];~i;i=edge[i].next){ 114 int v=edge[i].to; 115 if(v==father) continue;//如果连接的是当前节点的父亲节点,则不处理 116 dfs1(v,u); 117 siz[u]+=siz[v];//直接子树节点相加,当前节点的size加上子节点的size 118 if(siz[v]>siz[son[u]]){//如果没有设置过重节点son或者子节点v的size大于之前记录的重节点son,进行更新,保存重儿子 119 son[u]=v;//标记u的重儿子为v 120 } 121 } 122 } 123 124 void dfs2(int u,int tp) 125 { 126 top[u]=tp;//标记每个重链的顶端 127 tid[u]=++cnt;//每个节点剖分以后的新编号(dfs的执行顺序) 128 wt[cnt]=w[u];//新编号的对应权值 129 if(!son[u]) return ;//如果当前节点没有处在重链上,则不处理,否则就将这条重链上的所有节点都设置成起始的重节点 130 dfs2(son[u],tp);//搜索下一个重儿子 131 for(int i=head[u];~i;i=edge[i].next){ 132 int v=edge[i].to; 133 if(v==fa[u]||v==son[u]) continue;//处理轻儿子,如果连接节点不是当前节点的重节点并且也不是u的父节点,则将其的top设置成自己,进一步递归 134 dfs2(v,v);//每一个轻儿子都有一个从自己开始的链 135 } 136 } 137 138 void update_huo(int s,int t) 139 { 140 // int x=1,y=s; 141 for(int i=0;i<31;i++){ 142 if(t&(1<<i)){ 143 int v=s; 144 while(v){ 145 int father=top[v]; 146 int l=tid[father],r=tid[v]; 147 update(tree[i],lazy[i],l,r,1,1,n,1); 148 v=fa[father]; 149 } 150 151 // while(top[x]!=top[y]){ 152 // if(dep[top[x]]<dep[top[y]]) swap(x,y);//使x深度较大 153 // update(tree[i],lazy[i],tid[top[x]],tid[x],1,1,n,1); 154 // x=fa[top[x]]; 155 // } 156 // 157 // if(dep[x]>dep[y]) swap(x,y);//使x深度较小 158 // update(tree[i],lazy[i],tid[x],tid[y],1,1,n,1); 159 } 160 } 161 } 162 163 void update_yihuo(int s,int t) 164 { 165 // int x=1,y=s; 166 for(int i=0;i<31;i++){ 167 if(t&(1<<i)) continue; 168 int v=s; 169 while(v){ 170 int father=top[v]; 171 int l=tid[father],r=tid[v]; 172 update(tree[i],lazy[i],l,r,0,1,n,1); 173 v=fa[father]; 174 } 175 // while(top[x]!=top[y]){ 176 // if(dep[top[x]]<dep[top[y]]) swap(x,y);//使x深度较大 177 // update(tree[i],lazy[i],tid[top[x]],tid[x],0,1,n,1); 178 // x=fa[top[x]]; 179 // } 180 // 181 // if(dep[x]>dep[y]) swap(x,y);//使x深度较小 182 // update(tree[i],lazy[i],tid[x],tid[y],0,1,n,1); 183 } 184 } 185 186 //bool play_nim(int s,int t) 187 //{ 188 // int x=1,y=s; 189 // int flag=0; 190 // for(int i=0;i<31;i++){ 191 // int ans=0; 192 // while(top[x]!=top[y]){ 193 // if(dep[top[x]]<dep[top[y]]) swap(x,y);//使x深度较大 194 // ans+=query(tree[i],lazy[i],tid[top[x]],tid[x],1,n,1); 195 // x=fa[top[x]]; 196 // } 197 // 198 // if(dep[x]>dep[y]) swap(x,y);//使x深度较小 199 // ans+=query(tree[i],lazy[i],tid[x],tid[y],1,n,1); 200 // ans=ans&1; 201 // if((t&(1<<i)&&ans)||(!(t&(1<<i)&&!ans))){ 202 // flag=1;break; 203 // } 204 // } 205 // if(flag) return true; 206 // else return false; 207 //} 208 209 bool play_nim(int k,int s,int t) 210 { 211 // int x=1,y=s; 212 int ans=0; 213 while(s){ 214 int father=top[s]; 215 int l=tid[father],r=tid[s]; 216 ans+=query(tree[k],lazy[k],l,r,1,n,1); 217 s=fa[father]; 218 } 219 // while(top[x]!=top[y]){ 220 // if(dep[top[x]]<dep[top[y]]) swap(x,y);//使x深度较大 221 // ans+=query(tree[k],lazy[k],tid[top[x]],tid[x],1,n,1); 222 // x=fa[top[x]]; 223 // } 224 // 225 // if(dep[x]>dep[y]) swap(x,y);//使x深度较小 226 // ans+=query(tree[k],lazy[k],tid[x],tid[y],1,n,1); 227 228 ans=ans&1; 229 if((t&(1<<k))&&ans) return true; 230 if(!(t&(1<<k))&&!ans) return true; 231 return false; 232 } 233 234 int main() 235 { 236 scanf("%d%d",&n,&m); 237 init(); 238 for(int i=1;i<=n;i++) 239 scanf("%d",&w[i]);//点权 240 for(int i=1;i<n;i++){ 241 int u,v; 242 scanf("%d%d",&u,&v); 243 add(u,v); 244 add(v,u); 245 } 246 dfs1(1,0);//根节点 247 dfs2(1,1); 248 for(int i=1;i<=n;i++){//建树,每一位建一棵线段数 249 for(int k=0;k<31;k++){ 250 if(w[i]&(1<<k)) update(tree[k],lazy[k],tid[i],tid[i],1,1,n,1); 251 } 252 } 253 while(m--){ 254 int op,s,t; 255 scanf("%d%d%d",&op,&s,&t); 256 if(op==1){ 257 update_huo(s,t); 258 } 259 else if(op==2){ 260 update_yihuo(s,t); 261 } 262 // else if(op==3){ 263 // bool ans=play_nim(s,t); 264 // if(ans) printf("YES\n"); 265 // else printf("NO\n"); 266 // } 267 268 else if(op==3){ 269 int flag=0; 270 for(int i=0;i<31;i++){ 271 if(!play_nim(i,s,t)){ 272 flag=1;break; 273 } 274 } 275 if(flag) printf("YES\n"); 276 else printf("NO\n"); 277 } 278 } 279 return 0; 280 }