TYOI Day1 travel:Tree dp【處理重複走邊】

Leohh發表於2018-01-15

題意:

  給你一棵樹,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 }

 

相關文章