题目链接
好久没更新了……奇怪的py写完了,明天下午上台瞎说说晚上就能挂上来了(写的奇丑无比)……
题意:现在有一棵树,还有一只熊,这只熊每次可以从一个点跳到距离不超过k(k<=5)的点上。设f(x,y)表示从点x跳到y的最少次数,现让你求出所有点对(x,y),(其中x<y)的f值的和
思路:这题好难啊(其实是自己太菜了)……想得到是树形dp,但是想不出来怎么计数……日常看题解。
对于每一个点对,他们的贡献是[dis(x,y)/k],这个式子可以写成dis(x,y)/k+((k-dis(x,y)%k+k)%k)/k;后一项也就是把没达到k倍数的部分补全。
前一部分,我们先看dis(x,y)我们可以枚举每一条边,这条边指向的子树的个数乘上剩余点的个数,就是这条边的贡献。全部加起来最后再除以k就行了。
难的是第二部分,对于两个点x,y,我们当然不能爆搜。我们可以利用dfs后缀和的性质,将一棵子树中的所有点按%k的值保存。dp[i][j]表示以i为根节点的子树中与总根节点距离%k=j的节点的个数,那么对于一个点来说,他的所有子节点构成的子树只要以k^2复杂度相互计算就行了,以他本身为lca。这样既不会重复,也很好转移。这个不太好说,直接看代码比较容易。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include<stdio.h> #include<string.h> #include<vector> #include<algorithm> using namespace std; vector<int >g[200005]; long long dp[200005][10]; long long sum[200005]; long long n,m,k; long long ans=0; void dfs(int u,int fa,int deep) { dp[u][deep%k]=1; sum[u]=1; int l=g[u].size(); for(int i=0;i<l;i++) { int v=g[u][i]; if(v==fa)continue; dfs(v,u,deep+1); for(int a=0;a<=k-1;a++) for(int b=0;b<=k-1;b++) { long long dis=((a+b)%k-(2*deep)%k+k)%k; long long gong=(k-dis+k)%k; ans=ans+gong*dp[u][a]*dp[v][b]; } ans+=sum[v]*(n-sum[v]); sum[u]+=sum[v]; for(int j=0;j<k;j++)dp[u][j]+=dp[v][j]; } } int main() { scanf("%I64d%I64d",&n,&k); for(int i=1;i<=n-1;i++) { int u,v; scanf("%d%d",&u,&v); g[u].push_back(v); g[v].push_back(u); } dfs(1,-1,0); printf("%I64d\n",ans/k); return 0; }
|