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 }

 

posted @ 2018-01-15 22:04  Leohh  阅读(249)  评论(0编辑  收藏  举报