链接:http://aiiage.hustoj.com/contest.php?cid=1000
题意:
给你n(n<=3000)个点 n-1 条边的一棵树,每条边有一个权值w(w<=1000),现在有Q个询问:在距离mod k的意义下,距离 u 点最远的点有多远?
第一行输入n,表示n个点
接下来n-1行,输入u,v,w,表示一条边,w是权值,点从1开始
输入一个Q,表示询问次数(Q<=1e5)
接下来Q行,输入u,k(2<=k<=100)
对于每次询问,输出在距离mod k的意义下,距离 u 点最远的点有多远
样例如下:
输入:
3
1 2 7
1 3 6
5
1 9
1 7
3 2
3 15
2 7
输出:
7
6
1
13
6
思路1:暴力算出所有点对 (u,v)之间的距离dis[u][v],对于每次询问(u,k),遍历一遍
dis[u][i]%k的值,取大。
时间复杂度:O(n*n + Q*n*k) ,超时
空间复杂度:O(n*n)
思路2:暴力算出所有点对(u,v)之间的距离dis(u,v),再算出对于每个可能的k值取模后的结果,在这个过程中对得到的结果取大。
时间复杂度:O(n*n*k+Q),超时
空间复杂度:O(n*k)
思路3:
1.我们把无根树看成有根树
2.对于结点u,满足mod k距离最大的结点要么在 u 的子树上,要么在 u 的父亲那头,我们先求在 u 的子树上的满足情况。(后面再解释)
3.设dp1[u][j][k]表示对于结点u,在u的子树上,满足距离%k 后,与 u 距离为 j 的结点数。
4.那么dp1[u][j][k]应该等于所有dp1[v][(j-w%k+k)%k][k]之和,其中v表示u的儿子,w表示(u,v)的距离。
为什么是(j-w%k+k)%k?
要得到u与v的子树里距离是j的结点个数,已知u与v距离为w,那么问题就变成了求v与v的子树里距离是j-w的结点个数。
5.知道子树里的还不行,我们不难得出,除了root之外,所有其他结点都还没考虑父亲另一头的结点。
6.这时候我们设dp[u][j][k]表示满足 dis(u,v)%k==j 的v的总个数。
7.我们已知子树里满足条件的结点数了,那么如何求出剩余的呢?设dis(u,u的父亲)=w,则父亲那头与u距离为j的结点数是:dp[father u][(j-w+k)%k][k]个。
8.这时候我们发现对于u的父亲,距离为j-w的结点不只是从另一边得到,还可能从u的子树得到,因此我们要减去这一部分重复的。设距离u的父亲为j-w的结点为v,所以dis(v,father u)==j-w,又知道dis(father u,u)==w,所以dis(u,v)的距离为j-2w,从前面已知
dp1[u][(j-2*w%k+k)%k][k],因此可以得到
dp[v][j][k]=dp1[v][j][k]+
dp[u][(j-w%k+k)%k][k]-
dp1[v][(j-(2*w)%k+2*k)%k][k];
时间复杂度:O(n*k*k+Q*k)
空间复杂度:O(n*k*k),如果用int会爆内存,在不影响答案正确性的情况下,把int改为short,就不会爆内存了
#include <bits/stdc++.h>
using namespace std;
struct node
{
short v,w;
node(){}
node(int v,int w):v(v),w(w){}
};
int ans[3001][101];
vector<node> g[3001];
short dp1[3001][101][101];
short dp[3001][101][101];
short fa[3001];
void getfa(int u,int pre)
{
int w;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i].v;
if(v==pre){w=g[u][i].w;continue;}
fa[v]=u;
getfa(v,u);
}
if(pre==-1)
{
for(int k=2;k<=100;k++)
for(int j=0;j<k;j++)
dp[u][j][k]+=dp1[u][j][k];
}
else
{
for(int k=2;k<=100;k++)
for(int j=0;j<k;j++)
dp1[pre][j][k]+=dp1[u][(j-w%k+k)%k][k];
}
}
void dfs(int u)
{
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i].v,w=g[u][i].w;
if(v==fa[u])continue;
for(int k=2;k<=100;k++)
for(int j=0;j<k;j++)
dp[v][j][k]=dp1[v][j][k]+
dp[u][(j-w%k+k)%k][k]-
dp1[v][(j-2*(w%k)+2*k)%k][k];
dfs(v);
}
}
int main()
{
int n;
scanf("%d",&n);
int u,v,w;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
g[u].push_back(node(v,w));
g[v].push_back(node(u,w));
}
for(u=1;u<=n;u++)
for(int k=2;k<=100;k++)
dp1[u][0][k]=1;
getfa(1,-1);
dfs(1);
for(u=1;u<=n;u++)
{
for(int k=2;k<=100;k++)
{
for(int j=k-1;j;j--)
{
if(dp[u][j][k])
{
ans[u][k]=j;
break;
}
}
}
}
int q;
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&u,&w);
printf("%d\n",ans[u][w]);
}
return 0;
}
/**************************************************************
Problem: 1005
User: xtuacm1
Language: C++
Result: 正确
Time:1130 ms
Memory:122556 kb
****************************************************************/