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$。。。不要思惟僵化