题目大意:
给你一颗n个节点的树,每条边有边权。
Q组询问,每次询问u,k,输出从u点出发在mod k意义下的最长路。
2<=n<=3000,1<=Q<=1e5,2<=k<=100
分析:
很容易想到预处理出dis[i][j]表示i到j的距离,对于每次询问O(n)查询,时间复杂度O(n^2+Qn),不可做。
考虑优化,用dp[u][k]表示从u出发在mod k意义下的最长路,对于每次询问O(1)查询,时间复杂度为O(n*n*k+Q),也不行。
上述两个算法超时的主要原因是没有对已有信息的再次利用,而是每次都重新开始处理信息。
那么定义dp[i][j][k]表示从i点出发,在mod j意义下长度为k的路径是否存在(方案数)。
可以先从根节点dfs,记录从根节点出发的dp值,转移即为dp[x][j][(k+val[i])%j]+=dp[to[i]][j][k]。
然后再进行一遍dfs,对于一个不是根节点的点,它的子树的dp值显然是对的,但从该节点往上走的情况还没统计,因为是dfs更新,所以该节点的父亲已经更新过了,那么该节点的dp值加上它父节点的dp值即可。但其父节点的dp值同样包含了该节点的子树的dp值,所以在更新前其父节点要先减去改节点的dp值,更新完后再加回来。
对于每组询问,i从k-1枚举到0,看长度为i的路径是否存在,输出即可。处理每次询问最大是k的复杂度。
该算法复杂度是O(n*k*k+Qk),可以通过。
p.s. 在打模拟赛的时候因为思路以及方程和同机房某位dalao撞了导致被教练误以为抄袭,就当这题我现场没AC吧。
代码:
#include<bits/stdc++.h>
#define maxn 3005
#define maxm 6005
using namespace std;
typedef long long LL;
int read()
{
char c;int sum=0,f=1;c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int head[maxn],to[maxm],val[maxm],nex[maxm],cnt;
void add(int u,int v,int w)
{
to[++cnt]=v;nex[cnt]=head[u];val[cnt]=w;head[u]=cnt;
}
int n,q;
int dp[maxn][105][105],son[105][105];
void dfs1(int x,int fa)
{
for(int i=1;i<=100;i++)
dp[x][i][0]=1;
for(int i=head[x];i;i=nex[i])
{
if(to[i]==fa) continue;
dfs1(to[i],x);
for(int j=1;j<=100;j++)
for(int k=0;k<j;k++)
dp[x][j][(k+val[i])%j]+=dp[to[i]][j][k];
}
}
void dfs2(int x,int fa,int v)
{
if(fa!=-1)
{
for(int j=1;j<=100;j++)
for(int k=0;k<j;k++)
{
son[j][k]=dp[x][j][k];
dp[fa][j][(k+v)%j]-=son[j][k];
}
for(int j=1;j<=100;j++)
for(int k=0;k<j;k++)
dp[x][j][(k+v)%j]+=dp[fa][j][k];
for(int j=1;j<=100;j++)
for(int k=0;k<j;k++)
dp[fa][j][(k+v)%j]+=son[j][k];
}
for(int i=head[x];i;i=nex[i])
{
if(to[i]==fa) continue;
dfs2(to[i],x,val[i]);
}
}
int main()
{
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
n=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read(),w=read();
add(u,v,w);add(v,u,w);
}
dfs1(1,-1);
dfs2(1,-1,0);
q=read();
while(q--)
{
int u=read(),k=read();
for(int i=k-1;i>=0;i--)
if(dp[u][k][i])
{
printf("%d\n",i);
break;
}
}
return 0;
}