BZOJ3697 采药人的路径 【点分治】

题目

采药人的药田是一个树状结构,每条路径上都种植着同种药材。

采药人以自己对药材独到的见解,对每种药材进行了分类。大致分为两类,一种是阴性的,一种是阳性的。

采药人每天都要进行采药活动。他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径。采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的。他想知道他一共可以选择多少种不同的路径。

输入格式

第1行包含一个整数N。

接下来N-1行,每行包含三个整数a_i、b_i和t_i,表示这条路上药材的类型。

输出格式

输出符合采药人要求的路径数目。

输入样例

7

1 2 0

3 1 1

2 4 0

5 2 0

6 3 1

5 7 1

输出样例

1

提示

对于100%的数据,N ≤ 100,000。

题解

树上路径计数,联想到点分治

对于分治出来的,每一个子树的根,我们需要找出经过该根的满足条件的路径数

很容易想到,如果对两种权值赋值为-1和1的话,一条路径权值和为0一定是阴阳互补的

先不考虑休息站,如果求阴阳互补的路径数,只需要开一个数组记录各种权值出现的次数【当然由于有负数就加上个n】

对于根的每棵子树统计完时,先与之前统计的计算对答案的贡献,路经长为-x的个数乘以之前为x的个数即可

现在考虑休息站,意味着路径中出现中途已经出现0的情况,既然如此,那么休息站到根的权值和与该点到根的权值和一定相等

开一个数组记录dfs路径上各种权值出现的次数,看看该点到根是否存在这样可以作为休息站的点

这样所有的点就分为有休息站和没有休息站两种,分开组合统计,具体就自行思考吧【看代码也行】

复杂度\(O(nlogn)\)

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
using namespace std;
const int maxn = 100005,maxm = 200005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57) {if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - ‘0‘; c = getchar();}
    return out * flag;
}
LL ans;
int n,h[maxn],ne = 2;
struct EDGE{int to,nxt,w;}ed[maxm];
inline void build(int u,int v,int w){
    ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++;
    ed[ne] = (EDGE){u,h[v],w}; h[v] = ne++;
}
int sum,Siz[maxn],F[maxn],rt,vis[maxn];
void getRT(int u,int f){
    Siz[u] = 1; F[u] = 0;
    Redge(u) if (!vis[to = ed[k].to] && to != f){
        getRT(to,u);
        Siz[u] += Siz[to];
        F[u] = max(F[u],Siz[to]);
    }
    F[u] = max(F[u],sum - Siz[u]);
    if (F[u] < F[rt]) rt = u;
}
LL A[maxm],AA[maxm],B[maxm],BB[maxm];
int ex[maxm],d[maxn];
void dfs(int u,int f){
    int x = d[u] + n;
    if (ex[x]) BB[x]++;
    else B[x]++;
    ex[x]++;
    Redge(u) if (!vis[to = ed[k].to] && to != f){
        d[to] = d[u] + ed[k].w;
        dfs(to,u);
    }
    ex[x]--;
}
void DFS(int u,int f){
    Siz[u] = 1;
    Redge(u) if (!vis[to = ed[k].to] && to != f){
        DFS(to,u);
        Siz[u] += Siz[to];
    }
}
void solve(int u){
    vis[u] = true;
    DFS(u,0);
    Redge(u) if (!vis[to = ed[k].to]){
        for (int i = 0; i <= Siz[to]; i++)
            B[n + i] = B[n - i] = BB[n + i] = BB[n - i] = 0;
        d[to] = ed[k].w;
        dfs(to,u);
        ans += BB[n] + BB[n] * A[n] + B[n] * AA[n] + BB[n] * AA[n] + B[n] * A[n];
        for (int i = 1; i <= Siz[to]; i++){
            ans += BB[n - i] * A[n + i] + B[n - i] * AA[n + i] + BB[n - i] * AA[n + i];
            ans += BB[n + i] * A[n - i] + B[n + i] * AA[n - i] + BB[n + i] * AA[n - i];
        }
        A[n] += B[n]; AA[n] += BB[n];
        for (int i = 1; i <= Siz[to]; i++){
            A[n - i] += B[n - i];
            AA[n - i] += BB[n - i];
            A[n + i] += B[n + i];
            AA[n + i] += BB[n + i];
        }
    }
    for (int i = 0; i <= Siz[u]; i++)
        A[n - i] = A[n + i] = AA[n - i] = AA[n + i] = 0;
    Redge(u) if (!vis[to = ed[k].to]){
        sum = Siz[to]; F[rt = 0] = INF;
        getRT(to,0);
        solve(rt);
    }
}
int main(){
    n = read(); int a,b,w;
    for (int i = 1; i < n; i++){
        a = read(); b = read(); w = read();
        build(a,b,w ? 1 : -1);
    }
    sum = n; F[rt = 0] = INF;
    getRT(1,0);
    solve(rt);
    cout << ans << endl;
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8467001.html

时间: 2024-11-06 16:11:31

BZOJ3697 采药人的路径 【点分治】的相关文章

【BZOJ3697】采药人的路径 点分治

[BZOJ3697]采药人的路径 Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材.采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的.采药人每天都要进行采药活动.他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径.采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的.他想知道他一共可以选择多少种

BZOJ3697 采药人的路径

点分治...尼玛啊!蒟蒻怎么做的那么桑心%>_<% Orz hzwer 蒟蒻就补充一下hzwer没讲的东西: (1)对于阴性的植物权值设为-1,阳性的设为+1 (2)最后一段就是讲如何利用新的子树的f[]值求出ans和更新g[] 1 /************************************************************** 2 Problem: 3697 3 User: rausen 4 Language: C++ 5 Result: Accepted 6

【BZOJ-3697&amp;3127】采药人的路径&amp;YinandYang 点分治 + 乱搞

3697: 采药人的路径 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 681  Solved: 246[Submit][Status][Discuss] Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材.采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的.采药人每天都要进行采药活动.他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径.

【BZOJ】【3697】采药人的路径 &amp; 【3127】【USACO2013 Open】Yin and Yang

点分治 Orz hzwer 倒是比较好想到点分治……然而在方案统计这里,我犯了两个错误…… 1.我比较傻逼的想的是:通过儿子来更新父亲,也就是统计以x为根的子树中xxxx的路径有多少条……这样转移. 然而这实在是太傻逼了,黄学长教做人:从父亲来更新儿子,走到一个节点直接更新路径的统计数,反正我们要的是[经过root的xx路径的数量] 所以可以一遍dfs直接搞出来…… 2.统计方案的方式也想错了……我只考虑了以root作为中转站的路径,然而经过root的路径中,并不只有这种路径是合法的……中转站在

P4930「FJ2014集训」采药人的路径

题目:P4930「FJ2014集训」采药人的路径 思路: 这篇不算题解,是让自己复习的,什么都没说清楚. 很久没有写点分治了,以前为了赶课件学的太急,板子都没打对就照着题解写题,导致学得很不扎实. 这道题差不多是在郭老师的指导下一点点凑出来的,还是没能自己完整写出一道题,惭愧. 这道题大意是:给出一棵边权为0/1的树,求满足以下条件的路径总数:0的个数等于1的个数,且路径上存在一点到路径两端也满足该条件. 这种求路径总数的题,可以想到用点分治. 把0看作-1,就可以转化为路径边权和为0. 如果没

【bzoj3697】采药人的路径 树的点分治

题目描述 给出一棵 $n$ 个点的树,每条边的边权为1或0.求有多少点对 $(i,j)$ ,使得:$i$ 到 $j$ 的简单路径上存在点 $k$ (异于 $i$ 和 $j$ ),使得 $i$ 到 $k$ 的简单路径上0和1数目相等,$j$ 到 $k$ 的简单路径上0和1数目也相等. 输入 第1行包含一个整数N.接下来N-1行,每行包含三个整数a_i.b_i和t_i,表示这条路上药材的类型. 输出 输出符合采药人要求的路径数目. 样例输入 71 2 03 1 12 4 05 2 06 3 15 7

3697. 采药人的路径【点分治】

Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材. 采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的. 采药人每天都要进行采药活动.他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径.采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的.他想知道他一共可以选择多少种不同的路径. Input 第1

【BZOJ3784】树上的路径 点分治序+ST表

[BZOJ3784]树上的路径 Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值. Input 第一行两个正整数N,M 下面N-1行,每行三个正整数a,b,c(a,b<=N,C<=10000).表示结点a到结点b有一条权值为c的边. Output 共M行,如题所述. Sample Input 5 10 1 2 1

【BZOJ-3784】树上的路径 点分治 + ST + 堆

3784: 树上的路径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 462  Solved: 153[Submit][Status][Discuss] Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值. Input 第一行两个正整数N,M 下面N-1行,每行三个正整数a,