题目链接
当时比赛的时候没有写出来,当时,换根DP不太熟练,所以没怎么,往那方面去想,想了一个类似树形DP,结果还是没写出来。
然后,仔细学了会换根DP,后面的沈阳网络赛就碰到一道换根DP的题,喜提一发PE AC
但是,还是想用自己最初的那个想法去做,并且A掉
牛客网1号就能开了省赛的复现赛 (????),国庆嘛,太高兴了,然后到今天才去写,啦啦啦啦
想法:
如上图,红色表示树的组成部分,蓝色表示路径的种类
在一棵子树中,将路径分为三种:
(1) 只有一条边
(2) 子树的根节点是路径的端点(至少由两条边组成)
(3) 子树的根节点不是路径的端点,(至少由两条边组成)
对于三种路径的答案的统计的方法:
令根节点是u, u的一个子节点是v, val 是边(u, v)的权值
(1) 在输入的时候,直接判断下就好了
(2) 就是节点v 所控制的子树中,能与val组成符合答案的个数
(3) 如图上的(3) 就是dp[5][3]*dp[6][2017]
代码中会有详细的注释
#include <bits/stdc++.h>
const int maxn = 2e4+50;
const int maxm = 2020;
using namespace std;
typedef long long ll;
struct note{
int to, w;
note(){}
note(int to1, int w1){
to = to1, w = w1;
}
};
vector <note> maps[maxn];
int n, in[maxn];
ll ans, dp[maxn][maxm], tmp[maxm];
// dp[u][i] 表示节点u控制的子树中,以u为端点的路径,值为i的种类数
void dfs(int u, int fa, int val){
//u dfs的当前节点, fa 节点u的父节点 val 边(u, fa)的权值
for(int i=0; i<2019; i++) //初始化
dp[u][i] = 0;
for(int i=0, len=maps[u].size(); i<len; i++){
int v = maps[u][i].to, w = maps[u][i].w;
if(v != fa){
dfs(v, u, w);
for(int j=0; j<2019; j++){
ans += dp[u][j]*dp[v][(2019-j)%2019]; //由(3)产生的答案数
dp[u][j] += dp[v][j]; //把子树的统计结果累加上去
}
}
}
if(in[u] != 1 && u != fa)
ans += dp[u][(2019-val)%2019]; //由(2)产生的答案数,但是不能统计叶子节点和整棵树的根节点
for(int i=0; i<2019; i++)
tmp[(i+val)%2019] = dp[u][i];
for(int i=0; i<2019; i++)
dp[u][i] = tmp[i];
dp[u][val%2019]++;
}
int main()
{
while(scanf("%d", &n)!=EOF){
ans = 0;
for(int i=0; i<=n; i++)
maps[i].clear(), in[i] = 0;
for(int i=1, a, b, c; i<n; i++){
scanf("%d%d%d", &a, &b, &c);
maps[a].push_back(note(b, c));
maps[b].push_back(note(a, c));
in[a]++, in[b]++;
if(c%2019 == 0)
ans++; //由(1)产生的答案数
}
dfs(1, 1, 2019);
printf("%lld
", ans);
}
return 0;
}
不懂的,可以和我交流