HNCPC 2019 I题 点分治+思维

传送门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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值