2019 湖南省赛 I题 2019 换根dp

题目:传送门

2019 湖南省赛 I题 2019 换根dp_第1张图片

题解:dp[i][j] 表示点 i 的子节点到点 i 的距离mod 2019 = j的路径个数。从节点 1 深搜一下, 可以得到以节点 1 与任一节点形成的距离对答案的贡献。 然后换根, 可以得到每个节点对答案的贡献, 但是每两个节点之间对答案的贡献都被计算了两次,所以ans / 2。

//http://acm.hnucm.edu.cn/JudgeOnline/problem.php?id=1527
#include
using namespace std;
typedef long long ll;
const int N = 2e4+10;
int n;
ll ans;
struct Edge{
	int to, next, w;	
}e[N<<1];
int head[N], tot;
void addEdge(int u, int v, int w){
	e[tot] = Edge{v, head[u], w};
	head[u] = tot++;
}
int dp[N][2020];

void pika(int u){
	ans += dp[u][0];
}
void dfs1(int u, int fa){
	for(int i = head[u]; i != -1; i = e[i].next){
		int v = e[i].to, w = e[i].w;
		if(v == fa) continue;
		dfs1(v, u);
		dp[u][w]++;
		for(int j = 0; j < 2019; j++){
			dp[u][(j+w)%2019] += dp[v][j];
		}
	}
}
void dfs2(int u, int fa){
	for(int i = head[u]; i != -1; i = e[i].next){
		int v = e[i].to, w = e[i].w;
		if(v == fa) continue;
		// 保存原节点信息
		int d1[2020], d2[2020];
		for(int j = 0; j < 2019; j++){
			d1[j] = dp[u][j], d2[j] = dp[v][j];
		}
		// 开始换根, 根 u 换成 v
		// u 中 减去 v 的贡献
		for(int j = 0; j < 2019; j++){
			dp[u][(j+w)%2019] -= dp[v][j];
		}
		dp[u][w]--;
		// v 中加上 u 的贡献
		dp[v][w]++;
		for(int j = 0; j < 2019; j++){
			dp[v][(j+w)%2019] += dp[u][j];
		}
		dfs2(v, u);
		pika(v); // 统计答案
		// 还原
		for(int j = 0; j < 2019; j++){
			dp[u][j] = d1[j], dp[v][j] = d2[j];
		}
	}
}

int main(){
	while(~scanf("%d", &n)){
		tot = 0, ans = 0;
		memset(head, -1, sizeof(int)*(n+1));
		for(int i = 0; i <= n; i++){
			for(int j = 0; j <= 2019; j++)
				dp[i][j] = 0;
		}
		int u, v, w;
		for (int i = 1; i < n; ++i){
			scanf("%d%d%d", &u, &v, &w);
			addEdge(u, v, w);
			addEdge(v, u, w);
		}
		dfs1(1, 0);
		pika(1);
		dfs2(1, 0);
		printf("%lld\n", ans/2);
	}
	return 0;
}

 

你可能感兴趣的:(树形dp)