P3727 曼哈顿计划E
题意:
给出一棵树,点有点权,给出一种博弈方式,问存不存在一条链,使在该链上博弈时,先手必败。
题解:
博弈上树。首先你须要会一些基本博弈,并打表发现它们的规律。写出一个$Get\-SG$函数。(打表真费劲)
1 int get_SG(int x,int op){ 2 if(op==1) return x; 3 else if(op==2){ 4 if(s%2==1) return x%2; 5 else{ 6 int len=s+1; 7 x%=len;if(x==0) x=len; 8 if(x<=len-2) return x%2; 9 else if(x==len-1) return 2; 10 else return 0; 11 } 12 } 13 else if(op==3){ 14 return x/s; 15 } 16 else if(op==4){ 17 if(x==0) return 0; 18 else if(x%4==1 || x%4==2) return x; 19 else if(x%4==3) return x+1; 20 else return x-1; 21 } 22 }
而后就能够重赋点权为该点的$SG$值了,这样问题就转变为了是否存在一条链,使得点权异或和为0。发现点权太大数组存不下,因此考虑挂链hash。而后就结束了

1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cstdio> 5 #define N 60011 6 #define MAX 1313131 7 #define int long long 8 using namespace std; 9 int val[N],dp[N],n,tot,indexx[N],s,siz[N],f[N],rt,sum,vist[N],dist[N],cnt,ans,ind[MAX*2],tt; 10 struct apple{ 11 int v,nxt,cnt; 12 }edge[N*4],ee[N*4]; 13 inline int read(){ 14 int ret=0,f=1;char ch=getchar(); 15 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 16 while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();} 17 return ret; 18 } 19 void add_to_hash(int x){ 20 ee[++tt].v=x; 21 ee[tt].nxt=ind[x%MAX]; 22 ee[tt].cnt=1; 23 ind[x%MAX]=tt; 24 } 25 int find_hash(int x){ 26 int t=ind[x%MAX],vv,ret=0; 27 while(t){ 28 vv=ee[t].v; 29 if(vv==x){ 30 return ee[t].cnt; 31 } 32 t=ee[t].nxt; 33 } 34 return 0; 35 } 36 void addedge(int x,int y){ 37 edge[++tot].v=y; 38 edge[tot].nxt=indexx[x]; 39 indexx[x]=tot; 40 } 41 int get_SG(int x,int op){ 42 if(op==1) return x; 43 else if(op==2){ 44 if(s%2==1) return x%2; 45 else{ 46 int len=s+1; 47 x%=len;if(x==0) x=len; 48 if(x<=len-2) return x%2; 49 else if(x==len-1) return 2; 50 else return 0; 51 } 52 } 53 else if(op==3){ 54 return x/s; 55 } 56 else if(op==4){ 57 if(x==0) return 0; 58 else if(x%4==1 || x%4==2) return x; 59 else if(x%4==3) return x+1; 60 else return x-1; 61 } 62 } 63 void find_rt(int u,int fa){ 64 siz[u]=1; 65 int t,vv; 66 f[u]=0; 67 t=indexx[u]; 68 while(t){ 69 vv=edge[t].v; 70 if(!vist[vv] && vv!=fa){ 71 find_rt(vv,u); 72 siz[u]+=siz[vv]; 73 f[u]=max(f[u],siz[vv]); 74 } 75 t=edge[t].nxt; 76 } 77 f[u]=max(f[u],sum-siz[u]); 78 if(f[u]<f[rt]) rt=u; 79 } 80 void dfs(int u,int fa,int dep){ 81 dist[++cnt]=dep; 82 int t,vv; 83 t=indexx[u]; 84 while(t){ 85 vv=edge[t].v; 86 if(vv!=fa && vist[vv]==0){ 87 dfs(vv,u,dep^dp[vv]); 88 } 89 t=edge[t].nxt; 90 } 91 } 92 inline int solve(int u,int x){ 93 int ret=0; 94 cnt=0; 95 dfs(u,0,0); 96 tt=0; 97 for(int i=2;i<=cnt;i++){ 98 if(find_hash(dist[i]^x)){ 99 int t,vv; 100 t=ind[(dist[i]^x)%MAX]; 101 while(t){ 102 vv=ee[t].v; 103 if(vv==(dist[i]^x)){ 104 edge[t].cnt++; 105 break; 106 } 107 t=ee[t].nxt; 108 } 109 } 110 else add_to_hash(dist[i]^x); 111 } 112 for(int i=2;i<=cnt;i++){ 113 ret+=find_hash(dist[i]); 114 }tot=0; 115 for(int i=2;i<=cnt;i++){ 116 ind[(dist[i]^x)%MAX]=0; 117 } 118 return ret; 119 } 120 void work(int u){ 121 vist[u]=1; 122 ans+=solve(u,dp[u]); 123 int t,vv; 124 t=indexx[u]; 125 while(t){ 126 vv=edge[t].v; 127 if(!vist[vv]){ 128 ans-=solve(vv,dp[u]); 129 sum=siz[vv]; 130 rt=0; 131 find_rt(vv,u); 132 work(rt); 133 } 134 t=edge[t].nxt; 135 } 136 } 137 signed main(){ 138 int op,T,x,y; 139 T=read(); 140 while(T--){ 141 memset(indexx,0,sizeof(indexx));tot=0; 142 ans=0; 143 memset(vist,0,sizeof(vist)); 144 n=read(); 145 for(int i=1;i<n;i++){ 146 x=read();y=read(); 147 addedge(x,y); 148 addedge(y,x); 149 } 150 for(int i=1;i<=n;i++){ 151 val[i]=read(); 152 } 153 op=read(); 154 if(op==2 || op==3) s=read(); 155 for(int i=1;i<=n;i++){ 156 dp[i]=get_SG(val[i],op); 157 } 158 f[0]=0x7ffffffffffff; 159 rt=0; 160 sum=n; 161 find_rt(1,0); 162 work(rt); 163 if(ans) printf("Mutalisk ride face how to lose?\n"); 164 else printf("The commentary cannot go on!\n"); 165 } 166 return 0; 167 }
P4886 快递员
题意:
给出一棵树,有边权。给出若干个点对$(x,y)$,要求找出一个点$u$,使得对于全部给出点对$dist(x,u)+dist(y,u)$的最大值最小,输出那个最大值的最小值。
题解:
终于不是套路题了。这道题还有点意思,首先仍是要点分,算出u为该点时全部点对的答案(由两条链拼起来)。而后考虑一下在什么状况下答案不能够更优:
1.最大值由出现于两颗不一样子树的链拼起来获得。这样若是咱们移动$u$的话,不论是向$x$方向仍是向$y$方向,都会致使一条链变长,一条链变短,总长不变。若是向其余方向移动的话必定更不优。
2.最大值有多组,且它们不位于同一棵子树内。同理也是,无论向哪一个方向移动都会使至少一个链组增加,变得更大。
这两种状况能够直接输出答案。除了这两种状况的话就还剩下每组最大值的两条链都出现同一棵子树里,那么咱们只要把$u$移向那个子树方向中的重心就好了。
感受仍是有点意思,比花式模板要强一些。注意若是有一个端点在$u$点的状况,也算到不一样子树里。

1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <vector> 5 #define N 100011 6 #define INF 0x7f7f7f7f 7 using namespace std; 8 struct apple{ 9 int v,nxt,q; 10 }edge[N*4]; 11 int indexx[N],vist[N],dist[N],col[N],n,m,Q[N][2],tot,f[N],sum,siz[N],rt,used[N],ans=INF; 12 void addedge(int x,int y,int z){ 13 edge[++tot].v=y; 14 edge[tot].q=z; 15 edge[tot].nxt=indexx[x]; 16 indexx[x]=tot; 17 } 18 void find_rt(int u,int fa){ 19 int t,vv; 20 t=indexx[u]; 21 f[u]=0;siz[u]=1; 22 while(t){ 23 vv=edge[t].v; 24 if(!vist[vv] && vv!=fa){ 25 find_rt(vv,u); 26 siz[u]+=siz[vv]; 27 f[u]=max(f[u],siz[vv]); 28 } 29 t=edge[t].nxt; 30 } 31 f[u]=max(f[u],sum-siz[u]); 32 if(f[u]<f[rt]) rt=u; 33 } 34 void dfs(int u,int fa,int c){ 35 if(fa==0) c=0; 36 int t,vv; 37 col[u]=c; 38 t=indexx[u]; 39 while(t){ 40 vv=edge[t].v; 41 if(vv!=fa){ 42 dist[vv]=dist[u]+edge[t].q; 43 if(fa==0) c++; 44 dfs(vv,u,c); 45 } 46 t=edge[t].nxt; 47 } 48 } 49 int solve(int u,int &maxx){ 50 int cnt=0,k=0; 51 maxx=0; 52 dist[u]=0; 53 dfs(u,0,0); 54 for(int i=1;i<=m;i++){ 55 int x=Q[i][0]; 56 int y=Q[i][1]; 57 used[i]=0; 58 if(dist[x]+dist[y]>maxx){ 59 maxx=dist[x]+dist[y]; 60 used[i]=++cnt; 61 } 62 else if(dist[x]+dist[y]==maxx){ 63 used[i]=cnt; 64 } 65 } 66 for(int i=1;i<=m;i++){ 67 int x=Q[i][0]; 68 int y=Q[i][1]; 69 if(used[i]==cnt){ 70 if(col[x]!=col[y]) return -1; 71 else if(k && col[x]!=k) return -1; 72 k=col[x]; 73 } 74 } 75 return k; 76 } 77 void work(int u){ 78 vist[u]=1; 79 int maxx=0,t=indexx[u],vv; 80 int k=solve(u,maxx); 81 ans=min(ans,maxx); 82 while(t){ 83 vv=edge[t].v; 84 if(col[vv]==k && !vist[vv]){ 85 rt=0;sum=siz[vv]; 86 find_rt(vv,u); 87 work(rt); 88 } 89 t=edge[t].nxt; 90 } 91 } 92 int main(){ 93 int x,y,z; 94 scanf("%d%d",&n,&m); 95 for(int i=1;i<n;i++){ 96 scanf("%d%d%d",&x,&y,&z); 97 addedge(x,y,z); 98 addedge(y,x,z); 99 } 100 for(int i=1;i<=m;i++){ 101 scanf("%d%d",&x,&y); 102 Q[i][0]=x; 103 Q[i][1]=y; 104 } 105 rt=0;f[0]=INF; 106 sum=n; 107 find_rt(1,0); 108 work(rt); 109 printf("%d",ans); 110 return 0; 111 }
P3714 [BJOI2017]树的难题
题意:
给你一棵 n 个点的无根树。
树上的每条边具备颜色。一共有 m 种颜色,编号为 1 到 m。第 i 种颜色的权值为 ci。
对于一条树上的简单路径,路径上通过的全部边按顺序组成一个颜色序列,序列能够划分红若干个相同颜色段。
定义路径权值为颜色序列上每一个同颜色段的颜色权值之和。
计算通过边数在 l 到 r 之间的全部简单路径中,路径权值的最大值。
题解:
这道题也没那么模板。挺有意思的,加了一个颜色限制,加了一个长度限制。
能够有感受,这个在处理的时候确定不能用去重写法,因此咱们考虑分开儿子讨论。
咱们假设儿子的颜色为直接连向儿子的边的颜色,那么对于两条树链合并时,就要看儿子的颜色是否同样,同样的话就要减去一个贡献。
那也不能暴力枚举啊,因此咱们把儿子按颜色排序,暂时记录一下相同颜色儿子的信息。这样发现当前儿子颜色和上一个儿子颜色不一样时就把暂时记录的信息和另外一组合并。
而后由于还有长度要求啊,是一段区间,因此咱们考虑线段树。要区间求最大值。
那么咱们开两棵线段树,一棵用来记录颜色相同,一棵用来记录颜色不一样的,而后能够启发式一下,按颜色为第一关键字,重量为第二关键字排个序,再线段树合并就好了。
感受想到点分以后就没那么难了呢。(彻底忘了当时的困难)

1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <vector> 5 #define N 100011 6 #define INF 0x7f7f7f7f 7 using namespace std; 8 struct apple{ 9 int v,nxt,q; 10 }edge[N*4]; 11 int indexx[N],vist[N],dist[N],col[N],n,m,Q[N][2],tot,f[N],sum,siz[N],rt,used[N],ans=INF; 12 void addedge(int x,int y,int z){ 13 edge[++tot].v=y; 14 edge[tot].q=z; 15 edge[tot].nxt=indexx[x]; 16 indexx[x]=tot; 17 } 18 void find_rt(int u,int fa){ 19 int t,vv; 20 t=indexx[u]; 21 f[u]=0;siz[u]=1; 22 while(t){ 23 vv=edge[t].v; 24 if(!vist[vv] && vv!=fa){ 25 find_rt(vv,u); 26 siz[u]+=siz[vv]; 27 f[u]=max(f[u],siz[vv]); 28 } 29 t=edge[t].nxt; 30 } 31 f[u]=max(f[u],sum-siz[u]); 32 if(f[u]<f[rt]) rt=u; 33 } 34 void dfs(int u,int fa,int c){ 35 if(fa==0) c=0; 36 int t,vv; 37 col[u]=c; 38 t=indexx[u]; 39 while(t){ 40 vv=edge[t].v; 41 if(vv!=fa){ 42 dist[vv]=dist[u]+edge[t].q; 43 if(fa==0) c++; 44 dfs(vv,u,c); 45 } 46 t=edge[t].nxt; 47 } 48 } 49 int solve(int u,int &maxx){ 50 int cnt=0,k=0; 51 maxx=0; 52 dist[u]=0; 53 dfs(u,0,0); 54 for(int i=1;i<=m;i++){ 55 int x=Q[i][0]; 56 int y=Q[i][1]; 57 used[i]=0; 58 if(dist[x]+dist[y]>maxx){ 59 maxx=dist[x]+dist[y]; 60 used[i]=++cnt; 61 } 62 else if(dist[x]+dist[y]==maxx){ 63 used[i]=cnt; 64 } 65 } 66 for(int i=1;i<=m;i++){ 67 int x=Q[i][0]; 68 int y=Q[i][1]; 69 if(used[i]==cnt){ 70 if(col[x]!=col[y]) return -1; 71 else if(k && col[x]!=k) return -1; 72 k=col[x]; 73 } 74 } 75 return k; 76 } 77 void work(int u){ 78 vist[u]=1; 79 int maxx=0,t=indexx[u],vv; 80 int k=solve(u,maxx); 81 ans=min(ans,maxx); 82 while(t){ 83 vv=edge[t].v; 84 if(col[vv]==k && !vist[vv]){ 85 rt=0;sum=siz[vv]; 86 find_rt(vv,u); 87 work(rt); 88 } 89 t=edge[t].nxt; 90 } 91 } 92 int main(){ 93 int x,y,z; 94 scanf("%d%d",&n,&m); 95 for(int i=1;i<n;i++){ 96 scanf("%d%d%d",&x,&y,&z); 97 addedge(x,y,z); 98 addedge(y,x,z); 99 } 100 for(int i=1;i<=m;i++){ 101 scanf("%d%d",&x,&y); 102 Q[i][0]=x; 103 Q[i][1]=y; 104 } 105 rt=0;f[0]=INF; 106 sum=n; 107 find_rt(1,0); 108 work(rt); 109 printf("%d",ans); 110 return 0; 111 }
总结
点分在处理树链的问题仍是很强的,可是也不要为了写点分而忘记了树形$dp$。。。不要思惟僵化