解法:樹形dp,dp[k][j][i]表示模k意義下以i爲終點長度爲i的路徑是否存在。
轉移:因爲每次最多走一條邊,所以只需要枚舉k轉移即可,方程見代碼,很容易想到。邊界條件就是能直接到的點的dp距離值=1。代碼中爲了簡化,使用了三個數組,循環使用,來表示當前點狀態,後續狀態,總狀態。這樣就能使寫代碼快很多,還有就是注意,後續狀態轉移完之後,還要再枚舉一次,得到完整的後續狀態,不然會WA!
ps:從標程的寫法上漲姿勢了,終於懂得如何在一堆數組中簡化自己的代碼了!
代碼:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define register int int
using namespace std;
vector<pair<int,int> >e[3005];
int n,q,sta[3005],cost[3005],uu,vv,ww,u[100005],k[100005],maxk;
bool useing[3005],nex[3005][105],dp[105][3005][105];
inline int read()
{
int res=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
res=res*10+c-'0';
c=getchar();
}
return res*f;
}
void init(int now,int fa,int k,bool f[3005][105])
{
f[now][0]=1;
for(int i=0,len=e[now].size();i<len;i++)
{
int to=e[now][i].first,pay=e[now][i].second;
if(to==fa) continue;
init(to,now,k,f);
for(int j=0;j<k;j++)
if(f[to][j])
f[now][(j+pay)%k]=1;
}
}
void DP(int now,int fa,int k,bool f[3005][105])
{
int head=0;
for(int i=0,len=e[now].size();i<len;i++)
{
int to=e[now][i].first,pay=e[now][i].second;//把除了父親之外的出邊保存到一個棧裏,之後更新
head++;
sta[head]=to;
cost[head]=pay;
}
for(int i=0;i<k;i++)
{
useing[i]=nex[now][i];//useing用來更新nex,nex用來更新dp,這一輪的nex就是下一輪的dp,所以再次更新,循環往復
f[now][i]|=nex[now][i];//useing是當前狀態,nex是後續狀態,dp是整個狀態,所以分別是1,2,3維
}
useing[0]|=1;
for(int i=1;i<=head;i++)
{
int v=sta[i],pay=cost[i];
for(int j=0;j<k;++j)
nex[v][(j+pay)%k]|=useing[j];//模k意義下能到達的邊,更新
for(int j=0;j<k;++j)
useing[(j+pay)%k]|=f[v][j];
}
for(int i=0;i<k;i++)
useing[i]=nex[now][i];
useing[0]|=1;
for(int i=head;i>=1;i--)//剛纔是從前往後推,現在nex發生了變化,所以再次從後向前推一遍,以至於可以轉移所有狀態
{
int v=sta[i],pay=cost[i];
for(int j=0;j<k;++j)
nex[v][(j+pay)%k]|=useing[j];
for(int j=0;j<k;++j)
useing[(j+pay)%k]|=f[v][j];
}
for(int i=0,len=e[now].size();i<len;i++)//遍歷出邊
{
int to=e[now][i].first;
if(to!=fa) DP(to,now,k,f);
}
}
void solve(int k,bool f[3005][105])
{
init(1,0,k,f);
memset(nex,0,sizeof(nex));
DP(1,0,k,f);
}
int main()
{
freopen("mtree.in","r",stdin); freopen("mtree.out","w",stdout);
n=read();
for(int i=1;i<n;i++)
{
uu=read();vv=read();ww=read();
e[uu].push_back(make_pair(vv,ww));
e[vv].push_back(make_pair(uu,ww));
}
q=read();
for(int i=1;i<=q;i++)
{
u[i]=read();k[i]=read();
maxk=max(maxk,k[i]);
}
for(int i=1;i<=maxk;i++)
solve(i,dp[i]);
for(int i=1;i<=q;i++)
for(int j=k[i]-1;j>=0;j--)
if(dp[k[i]][u[i]][j])
{
printf("%d\n",j);
break;
}
}