校内训练0609 problem c

【题目大意】

给一棵树,求有多少条路径满足总和-最大值 是P的倍数

n<=10^5, P<=10^7

【题解】

一看就是点分治嘛

不考虑子树合并,考虑poj1741的做法,每次考虑经过重心的路径,用优先队列,从小到达添加并求答案即可。

容斥下。

# include <queue>
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N = 1e5 + 10, M = 2e5 + 10, MAX = 1e7 + 5;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, P, v[N];
int head[N], nxt[M], to[M], tot;
inline void add(int u, int v) {
    ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v;
}
inline void adde(int u, int v) {
    add(u, v), add(v, u);
}

namespace DFZ {
    bool vis[N];
    int sz[N], mx[N];
    inline void dfsSize(int x, int fa = 0) {
        sz[x] = 1, mx[x] = 0;
        for (int i=head[x]; i; i=nxt[i]) {
            if(to[i] == fa || vis[to[i]]) continue;
            dfsSize(to[i], x);
            sz[x] += sz[to[i]];
            if(sz[to[i]] > mx[x]) mx[x] = sz[to[i]];
        }
    }
    int mi, centre;
    inline void dfsCentre(int x, int tp, int fa = 0) {
        if(sz[tp] - sz[x] > mx[x]) mx[x] = sz[tp] - sz[x];
        if(mx[x] < mi) mi = mx[x], centre = x;
        for (int i=head[x]; i; i=nxt[i]) {
            if(to[i] == fa || vis[to[i]]) continue;
            dfsCentre(to[i], tp, x);
        }
    }

    struct pa {
        int x, s, mx, fa;
        pa() {}
        pa(int x, int s, int mx, int fa) : x(x), s(s), mx(mx), fa(fa) {}
        friend bool operator < (pa a, pa b) {
            return a.mx > b.mx;
        }
    };

    priority_queue<pa> q;
    int buc[MAX];
    int st[M], stn;

    inline void delAns(int x, int s, int fa) {
        -- buc[s];
        for (int i=head[x]; i; i=nxt[i]) {
            if(to[i] == fa || vis[to[i]]) continue;
            delAns(to[i], (s + v[to[i]]) % P, x);
        }
    }

    inline ll doit(int x, int temp_s, int temp_mx, int temp_fa, int Vx) {
        ll ret = 0; (temp_s += v[x]) %= P; temp_mx = max(temp_mx, v[x]);
        while(!q.empty()) q.pop(); stn = 0;
        q.push(pa(x, temp_s, temp_mx, temp_fa));
        while(!q.empty()) {
            pa tp = q.top(); q.pop();
            // tp.s + S - Vx - mx = 0 (mod P)
            // S = Vx + mx - tp.s
            ret += buc[((tp.mx + Vx - tp.s) % P + P) % P];
            ++ buc[tp.s];
            st[++stn] = tp.s;
            for (int i=head[tp.x]; i; i=nxt[i]) {
                if(to[i] == tp.fa || vis[to[i]]) continue;
                q.push(pa(to[i], (tp.s + v[to[i]]) % P, max(tp.mx, v[to[i]]), tp.x));
            }
        }
        for (int i=stn; i; --i) -- buc[st[i]];
        return ret;
    }

    ll ans;
    inline void dfs(int x) {
        dfsSize(x); mi = n;
        dfsCentre(x, x);
        x = centre;
        // ===== //
        // printf("x = %d\n", x);
        ans += doit(x, 0, 0, 0, v[x]);
        // ===== //
        vis[x] = 1;
        for (int i=head[x]; i; i=nxt[i])
            if(!vis[to[i]]) {
                ans -= doit(to[i], v[x], v[x], x, v[x]);
                dfs(to[i]);
            }
    }

    inline void main() {
        ans = 0;
        dfs(1);
        ans += n;
        cout << ans << endl;
    }
}

int main() {
    freopen("c.in", "r", stdin);
    freopen("c.out", "w", stdout);
    cin >> n >> P;
    for (int i=1, u, tv; i<n; ++i) {
        scanf("%d%d", &u, &tv);
        adde(u, tv);
    }
    for (int i=1; i<=n; ++i) scanf("%d", v+i);
    DFZ::main();
    return 0;
}
/*
5 2
1 2
1 3
2 4
3 5
1 3 3 1 2
*/

时间: 2024-12-23 14:21:38

校内训练0609 problem c的相关文章

「csp校内训练 2019-10-24」解题报告

「csp校内训练 2019-10-24」解题报告 T1.猴猴吃苹果 \(Description\) 猴猴最喜欢在树上玩耍,一天猴猴又跳上了一棵树,这棵树有 \(N \ (N \leq 50000)\) 个苹果,每个苹果有一个编号,分别为 \(0\) ~ \(N - 1\) 它们之间由 \(N-1\) 个树枝相连,猴猴可以从树枝的一端爬到树枝的另一端,所以猴猴可以从任意一个苹果的位置出发爬到任意猴猴想去的苹果的位置. 猴猴开始在编号为 \(K \ (K < N)\) 的苹果的位置,并且把这个苹果吃

「csp校内训练 2019-10-30」解题报告

「csp校内训练 2019-10-30」解题报告 T1.树 题目链接(逃) \(Description\): 现在有一棵树,共 \(N\) 个节点. 规定:根节点为 \(1\) 号节点,且每个节点有一个点权. 现在,有 \(M\) 个操作需要在树上完成,每次操作为下列三种之一: \(1 \ x \ a\):操作 \(1\),将节点 \(x\) 点权增加 \(a\). \(2 \ x \ a\):操作 \(2\),将以节点 \(x\) 为根的子树中所有点的权值增加 \(a\). \(3 \ x\)

2017-4-7校内训练

丧病hzwer的ctsc训练赛 My AC:3/4 A.[Ctsc2014]企鹅QQ 思路:乱hash,我比较菜,写的丑代码各种WA+TLE,好久才A掉. #include<cstdio> #include<algorithm> using namespace std; #define ll long long #define MN 200 #define MX 6000000 #define MM 9000001 #define MOD1 890123798112473LL st

「校内训练 2019-04-23」越野赛车问题 动态dp+树的直径

题目传送门 http://192.168.21.187/problem/1236 http://47.100.137.146/problem/1236 题解 题目中要求的显然是那个状态下的直径嘛. 所以这道题有一个非常简单的做法--线段树分治. 直接把每一条边按照 \(l, r\) 的区间放到线段树上进行分治,遍历的时候用并查集维护直径就可以了. 时间复杂度为 \(O(n\log^2n)\). 很早以前就写了这个算法,代码附在了最后,不多讲了. 但是这道题还有一个方法--动态 DP. 线段树分治

20170910校内训练

CCT 最近学校又发了n本五三题霸,BBS看到后十分高兴.但是,当他把五三拿到手后才发现,他已经刷过这些书了!他又认真地看了一会儿,发现新发的这些五三是2017版的,而他刷的是2016版的.现在他想找出所有他没有刷过的题来刷.每本五三都有m道题,并且它的特征(即它和去年版本的五三的差距)可以用一个m位二进制数来代表,二进制位上的1代表该题不同,0代表该题相同.比如4(100)就代表题目3和去年的有不同.5(101)就代表题目1和题目3和去年的有不同.而BBS热衷于给自己找麻烦,他要选择连续一段的

2017-3-3校内训练

hzwer出丧题虐人啦 ACM赛制 4/7 A.恼人的青蛙 题目大意:给定N*M矩阵上K个点,定义一条合法路径为从矩形外一点沿一条直线穿过矩形,每次走相同长度且在矩形内每步都要踩在给定点上,问经过给定点最多的路径经过几个点(若小于3输出0)(N,M,K<=5000). 思路:把点按横坐标第一关键字纵坐标第二关键字排序,f[i][j]表示有一条到i的路径,i上一个点是j,此时路径经过点数,每次确定i,j后就可以根据i,j算出j再前一个点的坐标,直接转移,复杂度O(K^2).评测机极慢稍微卡卡常才能

【三中校内训练】旅行

[题解] 显然的这是一道树形DP的题目 这里令f[i][0]为从i出发向以它为根的子树里走直到不能走的最大.最小价值 (不能走是什么自己阅读题目) 令s为x的儿子,w[i][j]为i和j之间的边的长度,则 f[x][0]=max(f[s][1]+w[s][i]) f[x][1]=min(f[s][0]+w[s][i]) 显然的通过这种方法,我们可以得到60分 可是,如何优化到线性呢 我们考虑一个节点x,x向某条边走的情况出现了很多次,浪费了很多时间 我们定义h[x][0/1]为从x出发,经过x的

[3.10校内训练赛]

真的报警啦,hzwer又出一堆丧题虐人啦..... ------------------------------------------- A.[poj-1190]生日蛋糕 要做一个m层的蛋糕,每一层有高度和半径,且要分别比它上面的那一层的高度和半径大至少一,给定总体积n,求最小的侧面和顶上的面积之和m<=20,n<=10000 搜索....但是要加上比较强的剪枝. 1.如果此时的半径和高度无法建出剩余体积那么大的蛋糕,剪掉.这种情况我们不考虑半径和高度的减小,直接用((r-1)^2+(h-1

20170908校内训练

题意: 学过博弈论的同学都知道Nim游戏后手必胜的条件是异或和为0给定一棵树 ,支持修改单点点权,询问链上异或和 预处理每个点到根的路径的异或和由于异或的特殊性质,在求链x->y的异或和的时候,我们只需要知道x到根的异或和,y到根的异或和,将他们异或起来,最后异或上lca处的值即可. 如图,查询两个灰色节点的异或和 如果一个点的值被修改,那么它和它的子树到根的路径的异或和的值都会被修改 所以,我们维护一棵dfs序为下标的线段树,线段树的sum意义是它的区间的值的异或和 修改一个值,如果该值为x,