TYOI Day1 travel:Tree dp【处理重复走边】
题意:
给你一棵树,n个节点,每条边有长度。
然后有q组询问(u,k),每次问你:从节点u出发,走到某个节点的距离mod k的最大值。
题解:
对于无根树上的dp,一般都是先转成以1为根的有根树,然后分别从上到下和从下到上两遍dp。
另一个技巧是:处理重复走边的情况时,可以让dp值表示达到某种状态的方案数。
表示状态:
dp[i][j][k] = max dis
表示从i节点出发,走的距离mod k = j时的方案数
找出答案:
对于每次询问(u,k),答案为:满足dp[u][d][k]>0的最大的d值。
如何转移:
第一遍dfs:
dp[i][(j+len)%k][k] = ∑ dp[son][j][k]
只考虑从上往下的路径。
第二遍dfs:
dp[i][(j+len)%k][k] += dp[par][j][k]
dp[i][(j+len)%k][k] -= old[i][((j-len)%k+k)%k][k]
其中old[i][j][k]代表原来的dp,即只考虑从上往下时的dp。
减去old是因为要将会导致重复走边的方案删去。
边界条件:
dp[i][0][k] = 1
others = 0
复杂度:
Tree dp: O(n*k*k)
Query: O(q*k)
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <vector> 5 #define MAX_N 3005 6 #define MAX_K 105 7 8 using namespace std; 9 10 struct Edge 11 { 12 int dst; 13 int len; 14 Edge(int _dst,int _len) 15 { 16 dst=_dst; 17 len=_len; 18 } 19 Edge(){} 20 }; 21 22 int n,q; 23 int dp[MAX_N][MAX_K][MAX_K]; 24 int old[MAX_N][MAX_K][MAX_K]; 25 vector<Edge> edge[MAX_N]; 26 27 void read() 28 { 29 cin>>n; 30 int x,y,z; 31 for(int i=1;i<n;i++) 32 { 33 cin>>x>>y>>z; 34 edge[x].push_back(Edge(y,z)); 35 edge[y].push_back(Edge(x,z)); 36 } 37 } 38 39 void dfs1(int now,int p) 40 { 41 for(int i=0;i<edge[now].size();i++) 42 { 43 Edge temp=edge[now][i]; 44 if(temp.dst!=p) dfs1(temp.dst,now); 45 } 46 for(int k=1;k<=100;k++) 47 { 48 for(int i=0;i<edge[now].size();i++) 49 { 50 Edge temp=edge[now][i]; 51 if(temp.dst!=p) 52 { 53 for(int j=0;j<k;j++) 54 { 55 dp[now][(j+temp.len)%k][k]+=dp[temp.dst][j][k]; 56 } 57 } 58 } 59 } 60 } 61 62 void dfs2(int now,int p,int l) 63 { 64 if(p!=-1) 65 { 66 for(int k=1;k<=100;k++) 67 { 68 for(int j=0;j<k;j++) 69 { 70 old[now][j][k]=dp[now][j][k]; 71 } 72 } 73 for(int k=1;k<=100;k++) 74 { 75 for(int j=0;j<k;j++) 76 { 77 dp[now][(j+l)%k][k]+=dp[p][j][k]; 78 dp[now][(j+l)%k][k]-=old[now][((j-l)%k+k)%k][k]; 79 } 80 } 81 } 82 for(int i=0;i<edge[now].size();i++) 83 { 84 Edge temp=edge[now][i]; 85 if(temp.dst!=p) dfs2(temp.dst,now,temp.len); 86 } 87 } 88 89 void work() 90 { 91 memset(dp,0,sizeof(dp)); 92 for(int i=1;i<=n;i++) 93 { 94 for(int k=1;k<=100;k++) 95 { 96 dp[i][0][k]=1; 97 } 98 } 99 dfs1(1,-1); 100 dfs2(1,-1,0); 101 cin>>q; 102 int u,k; 103 while(q--) 104 { 105 cin>>u>>k; 106 for(int d=k-1;d>=0;d--) 107 { 108 if(dp[u][d][k]) 109 { 110 cout<<d<<endl; 111 break; 112 } 113 } 114 } 115 } 116 117 int main() 118 { 119 read(); 120 work(); 121 }