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

Description

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

Input

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

Output

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

Sample Input

7
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1

Sample Output

1

我可能学了假的点分治……
用g[i][0…1],f[i][0…1]分别表示
前面几个子树以及当前子树路径长度和为i的路径数目
0和1用于区分路径上是否存在前缀和为i的节点(也就是可以设立中转站的节点)
 那么当前子树的贡献就是f[0][0] * g[0][0] + Σf [i][0] * g [-i][1] + f[i][1] * g[-i][0] + f[i][1] * g[-i][1],
其中i的范围[-d,d],d为当前子树的深度。

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #define N (200000+100)
  5 using namespace std;
  6 struct node
  7 {
  8     int to,next,len;
  9 } edge[N*2];
 10 int n,k,sum,root,INF;
 11 long long ans,g[N][2],f[N][2];
 12 int t[N*2];
 13 int head[N],num_edge,max_depth;
 14 int depth[N],d[N],size[N],maxn[N],dis[N];
 15 bool vis[N];
 16
 17 void add(int u,int v,int l)
 18 {
 19     edge[++num_edge].to=v;
 20     edge[num_edge].len=l;
 21     edge[num_edge].next=head[u];
 22     head[u]=num_edge;
 23 }
 24
 25 void Get_root(int x,int fa)
 26 {
 27     size[x]=1;
 28     maxn[x]=0;
 29     for (int i=head[x]; i!=0; i=edge[i].next)
 30         if (!vis[edge[i].to] && edge[i].to!=fa)
 31         {
 32             Get_root(edge[i].to,x);
 33             size[x]+=size[edge[i].to];
 34             maxn[x]=max(maxn[x],size[edge[i].to]);
 35         }
 36     maxn[x]=max(maxn[x],sum-size[x]);
 37     if (maxn[x]<maxn[root]) root=x;
 38 }
 39
 40 void Calc(int x,int fa)
 41 {
 42     max_depth=max(max_depth,depth[x]);
 43     if (t[dis[x]]) f[dis[x]][1]++;
 44     else f[dis[x]][0]++;
 45     t[dis[x]]++;
 46     for (int i=head[x]; i!=0; i=edge[i].next)
 47         if (edge[i].to!=fa && !vis[edge[i].to])
 48         {
 49             depth[edge[i].to]=depth[x]+1;
 50             dis[edge[i].to]=dis[x]+edge[i].len;
 51             Calc(edge[i].to,x);
 52         }
 53     t[dis[x]]--;
 54 }
 55
 56 void Solve(int x)
 57 {
 58     vis[x]=true;
 59     g[n][0]=1;//关于这里为什么初始化为1的问题,想了好久,最后还是学姐给我的解答
 60     //http://blog.csdn.net/wu_tongtong/article/details/79428928
 61     //可能路径是从当前树根到某一个节点的时候,路径已经平衡,不需要去另一个子树里面找另一个链拼接了
 62     //所以这一部分的答案也要统计进去。
 63     int up=0;
 64     for (int i=head[x]; i!=0; i=edge[i].next)
 65         if (!vis[edge[i].to])
 66         {
 67             depth[edge[i].to]=1;
 68             dis[edge[i].to]=edge[i].len+n;
 69             max_depth=1;
 70             Calc(edge[i].to,0);
 71             up=max(up,max_depth);
 72
 73             ans+=(g[n][0]-1)*f[n][0];
 74             for (int j=-max_depth; j<=max_depth; ++j)
 75                 ans+=g[n-j][0]*f[n+j][1]+g[n-j][1]*f[n+j][0]+g[n-j][1]*f[n+j][1];
 76             for (int j=-max_depth; j<=max_depth; ++j)
 77             {
 78                 g[n-j][0]+=f[n-j][0];
 79                 g[n-j][1]+=f[n-j][1];
 80                 f[n-j][0]=f[n-j][1]=0;
 81             }
 82         }
 83     for (int i=-up; i<=up; ++i)
 84         g[n-i][0]=g[n-i][1]=0;
 85     for (int i=head[x]; i!=0; i=edge[i].next)
 86         if (!vis[edge[i].to])
 87         {
 88             sum=size[edge[i].to];
 89             root=0;
 90             Get_root(edge[i].to,0);
 91             Solve(root);
 92         }
 93 }
 94
 95 int main()
 96 {
 97     int u,v,l;
 98     scanf("%d",&n);
 99     sum=maxn[0]=n;
100     for (int i=1; i<=n-1; ++i)
101     {
102         scanf("%d%d%d",&u,&v,&l);
103         add(u,v,l==0?-1:1);
104         add(v,u,l==0?-1:1);
105     }
106     Get_root(1,0);
107     Solve(root);
108     printf("%lld",ans);
109 }

原文地址:https://www.cnblogs.com/refun/p/8684130.html

时间: 2024-08-30 06:33:57

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

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

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

【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 采药人的路径 【点分治】

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

【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

【BZOJ 3697】采药人的路径

题目链接: TP 题解: 调了好久233. 大概想一想就是树分,然后考虑这样路径(u,v)的特征,以根节点(root)切开,u到root的阴阳差值,和v到root巧合互为相反数,然后考虑要有一个点可作为休息点,即u/v到root的路径中要有一点x与u/v到root的阴阳差值相同,然后维护一下就好. 注意的是阴阳差为0的特判……写挂了调好久,对拍也不好写,真是恶心. 代码: 1 #define Troy 11/23 2 #define inf 0x7fffffff 3 4 #include <bi

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

【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