计蒜客 39272.Tree-树链剖分(点权)+带修改区间异或和 (The 2019 ACM-ICPC China Shannxi Provincial Programming ContestE.)

Tree

 

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 }

 

上一篇:2018-2019 ACM-ICPC, China Multi-Provincial Collegiate Programming Contest


下一篇:The 2019 ACM-ICPC China Shannxi Provincial Programming Contest B. Product(杜教筛+约数)