HUD 5593——ZYB's Tree——————【树形dp】 ZYB's Tree
Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 400 Accepted Submission(s): 114
Problem Description
i.
Input
In the first line there is the number of testcases 1000000
Output
For 100000 are only for two tests finally.
Sample Input
1
3 1 1 1
Sample Output
3
Source
题目大意:给你一棵无根树,定义了结点距离为任意结点对(i,j)在最短路径上的边的条数。让你求出每个结点的结点距离
小于等于K的结点个数,然后求异或值。
解题思路:我们的总体思路是,先任意定一个根,形成一棵有根树,对于任意的结点,我们可以从距离该结点为K的儿孙结点
和祖先结点两个方面去统计结点个数。我们定义dp[u][j]表示以某个结点u为根的子树中,距离小于等于j的结点个数。现在我们
已经解决了距离该结点为K的儿孙方面的个数,还有祖先方面的需要统计,那么我们可以从结点u向他的祖先结点走,会经过u的父亲
祖父,曾祖父...依次向上。向上走i层,该祖先结点表示为ff,第i-1 层的祖先结点表示为pff。
那么会增加dp[ff][K-i] - dp[pff][K-i-1]个结点。
另外:题中给的1结点的父亲结点0是不用参与运算的。当到达0结点时,可以跳出循环。
#include<stdio.h> #include<algorithm> #include<string.h> #include<vector> using namespace std; const int maxn = 550000; typedef long long LL; int N,K,A,B; int f[maxn], dp[maxn][20]; vector<int>G[maxn]; void dfs(int u,int fa){ for(int i = 0; i <= K; i++){ //初始化以u为子树的根,距离u为i时的结点个数为1 dp[u][i] = 1; } for(int i = 0; i < G[u].size(); i++){ int v = G[u][i]; if(v == fa) continue; dfs(v,u); for(int j = 0; j <= K; j++){ //统计u的所有子节点 dp[u][j+1] += dp[v][j]; } } } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d%d%d%d",&N,&K,&A,&B); for(int i = 0; i <= N+20; i++){ G[i].clear(); } f[1] = 0; for(int i = 2; i <= N; i++){ int ff = (int)(((LL)A*i+B)%((LL)i-1) + 1); //计算时会超int f[i] = ff; G[ff].push_back(i); } memset(dp,0,sizeof(dp)); dfs(1,0); int ans = 0, tmp; for(int u = 1; u <= N; u++){ tmp = dp[u][K]; //子孙方面个数 int ff = u; //初始化祖先结点 for(int i = 1; i <= K; i++){ if(i == K){ tmp += dp[f[ff]][K-i]; continue; } if(f[ff] == 0){ //已经到了0结点 break; } tmp += dp[f[ff]][K-i] - dp[ff][K-i-1]; //统计向上经过的满足条件的祖先会贡献多少个结点 ff = f[ff]; //向祖先走 } ans ^= tmp; //求每个结点的异或和 } printf("%d ",ans); } return 0; } /* 55 15 3 3 8 */