传送门https://ac.nowcoder.com/acm/contest/1099/I
本来这题应该是考的树形dp(但是dp永远的痛)就尝试用点分治写一下,改了好久才改出来
本题求的是树上两点之间的权值和是2019的倍数的路径有多少,那个u<v的条件其实就是让你一条路径只算一次,反着不算一条新的。这里最主要的代码就是下面
for(int j = 0; j < cnt; j ++){
int t = temp[j];
if(temp[j] > mod)
t = t % mod;
if(t == 0 || t == mod){
ans ++;
ans += judge[0];
ans += judge[mod];
//cout << ans << endl;
}
else if(judge[mod - t]){
ans += judge[mod - t];
}
}
for(int j = 0; j < cnt; j ++){
if(temp[j] > 2019)
temp[j] %= 2019;
que.push(temp[j]);
judge[temp[j]] ++;
}
temp[j]就是以rt为根节点的子树每个点到根节点的距离,如果这个距离与judge(之前保存的子树中点到rt的距离)之和是2019的倍数,ans就加上方案数,(a + x)% mod == 0这里已知a和mod求x,具体看代码不难理解
ac代码
#include<iostream>
#include<stdio.h>
#include<cmath>
#include<cstring>
#include<stack>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<climits>
//#include<bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define scanf1(a) scanf("%d",&a)
#define scanf2(a,b) scanf("%d%d",&a,&b)
#define scanf3(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define scanf4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define ll long long
using namespace std;
const int mx = 2e5 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 2019;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int head[mx];
struct my{
int to, next, w;
}edge[mx << 1];
int idx;
int n;
void add(int a, int b, int c){
edge[idx].to = b;
edge[idx].w = c;
edge[idx].next = head[a];
head[a] = idx ++;
}
int maxp[mx], siz[mx], dis[mx], temp[mx];
bool vis[mx];
int judge[mx];
int rt, cnt, sum;
int ans;
void getrt(int u, int f){//找树的重心
siz[u] = 1, maxp[u] = 0;
for(int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if(v == f || vis[v])continue;
getrt(v, u);
siz[u] += siz[v];
if(siz[v] > maxp[u])
maxp[u] = siz[v];//maxp是当前结点的子树最大值
}
maxp[u] = max(maxp[u], sum - siz[u]);
if(maxp[u] < maxp[rt])
rt = u;
}
void getdis(int u, int f){
temp[cnt ++] = dis[u];
for(int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if(vis[v] || v == f) continue;
dis[v] = dis[u] + edge[i].w;
getdis(v, u);
}
}
void solve(int u){
static queue<int> que;
for(int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if(vis[v]) continue;
cnt = 0;
dis[v] = edge[i].w;
getdis(v, u);
for(int j = 0; j < cnt; j ++){
int t = temp[j];
if(temp[j] > mod)
t = t % mod;
if(t == 0 || t == mod){
ans ++;
ans += judge[0];
ans += judge[mod];
//cout << ans << endl;
}
else if(judge[mod - t]){
ans += judge[mod - t];
}
}
for(int j = 0; j < cnt; j ++){
if(temp[j] > 2019)
temp[j] %= 2019;
que.push(temp[j]);
judge[temp[j]] ++;
}
}
while(!que.empty()){
judge[que.front()] = 0;
que.pop();
}
}
void divide(int u){//分治
vis[u] = true;
//judge[0] = 1;
solve(u);//计算经过根节点的路径
for(int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if(vis[v]) continue;
maxp[rt = 0] = sum = siz[v];
getrt(v, 0);
getrt(rt, 0);
divide(rt);
}
}
int main2(){
while(~scanf("%d", &n)){
ans = 0;
memset(head, -1, sizeof head);
memset(vis, false, sizeof vis);
memset(dis, 0, sizeof dis);
memset(siz, 0, sizeof siz);
idx = 0;
rt = 0;
int u, v, w;
for(int i = 1; i < n; i ++){
u = read(), v = read(), w = read();
add(u, v, w);
add(v, u, w);
}
maxp[0] = sum = n;
getrt(1, 0);//找重心
getrt(rt, 0);
divide(rt);
cout << ans << "\n";
}
return 0;
}
int main(){
//ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int _;//_ = read();
_ = 1;
while(_--)main2();return 0;
}