题目描述

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

输入

第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。

点分治经典题,因为答案符合逆运算,所以采用单步容斥统计答案。因为要求0,1边数相等,所以把0边的边权改为-1,这样只要路径和为0就是满足第一条的路径,又因为要有一个中间点,所以不妨设f[i][0/1]分别表示当前点到当前重心边权和为i且没有/有中间点的方案数;g[i][0/1]分别表示当前点到当前重心边权和为-i且没有/有中间点的方案数。注意这里的中间点表示到当前点路径和为0的点。下面就到了这道题的两个难点:1、当前重心到某个子节点路径和为0的答案数要单独统计,不能容斥(但其实好像是可以的,只是很麻烦没必要qwq),因为端点不能作为中间点。因此对于每个重心要单独统计(dfs一下就行了),但要在每条路径上第二个到重心路径和为0的点再计入答案(要将第一个点当中间点)。2、如何判断一个点到重心之间是否有中间点是本道题的重中之重。可以用一段区间l,r来表示从当前点到重心的路径上所有点到重心路径和的最大值和最小值,因为每条边都是±1,所以这个区间一定是连续的(即区间中所有路径和都是存在的),那么当遍历到一个点时如果这个点到重心的路径和在这段区间中就说明有中间点。解释一下为什么:如果这个路径和(设为x)存在过就说明之前路径和为x的这个点到当前点之间路径和为0(怎么样,是不是恍然大悟qwq)。统计之后一步容斥就ok了,但需要注意的是从重心dfs时要把区间初始值赋成1–-1(也就是一段不存在的区间)来防止把重心算进方案数。

最后附上代码(这道题和BZOJ3127是同一道题,双倍经验哦)。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int ma;
int n,k;
int tot;
int num;
long long ans;
int root;
int x,y,z;
int to[200010];
int mx[100010];
int val[200010];
int head[100010];
int next[200010];
int size[100010];
bool vis[100010];
long long f[100010][2];
long long g[100010][2];
void add(int x,int y,int v)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
val[tot]=v;
tot++;
next[tot]=head[y];
head[y]=tot;
to[tot]=x;
val[tot]=v;
}
void getroot(int x,int fa)
{
size[x]=1;
mx[x]=0;
for(int i=head[x];i;i=next[i])
{
if(to[i]!=fa&&!vis[to[i]])
{
getroot(to[i],x);
size[x]+=size[to[i]];
mx[x]=max(mx[x],size[to[i]]);
}
}
mx[x]=max(mx[x],num-size[x]);
if(!root||mx[x]<mx[root])
{
root=x;
}
}
void dfs(int x,int fa,int dis,int l,int r)
{
if(dis>=l&&dis<=r)
{
if(dis>=0)
{
f[dis][1]++;
}
else
{
g[-dis][1]++;
}
}
else
{
if(dis>=0)
{
f[dis][0]++;
}
else
{
g[-dis][0]++;
}
}
l=min(l,dis);
r=max(r,dis);
ma=max(ma,max(-l,r));
for(int i=head[x];i;i=next[i])
{
if(to[i]!=fa&&!vis[to[i]])
{
dfs(to[i],x,dis+val[i],l,r);
}
}
}
void calc(int x,int fa,int dis,int cnt)//统计以从重心到子节点为路径的答案
{
if(dis==0)
{
if(cnt>=2)
{
ans++;
}
cnt++;
}
for(int i=head[x];i;i=next[i])
{
if(!vis[to[i]]&&to[i]!=fa)
{
calc(to[i],x,dis+val[i],cnt);
}
}
}
void partition(int x)
{
vis[x]=1;
calc(x,0,0,0);
ma=0;
dfs(x,0,0,1,-1);
ans+=f[0][1]*(f[0][1]-1)/2;
f[0][0]=f[0][1]=0;
for(int i=1;i<=ma;i++)
{
ans+=f[i][1]*g[i][0]+f[i][0]*g[i][1]+f[i][1]*g[i][1];
f[i][1]=f[i][0]=g[i][1]=g[i][0]=0;
}
for(int i=head[x];i;i=next[i])
{
if(!vis[to[i]])
{
ma=0;
dfs(to[i],0,val[i],0,0);
ans-=f[0][1]*(f[0][1]-1)/2;
f[0][0]=f[0][1]=0;
for(int j=0;j<=ma;j++)
{
ans-=f[j][1]*g[j][0]+f[j][0]*g[j][1]+f[j][1]*g[j][1];
f[j][1]=f[j][0]=g[j][1]=g[j][0]=0;
}
num=size[to[i]];
root=0;
getroot(to[i],0);
partition(root);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
if(z==0)
{
z=-1;
}
add(x,y,z);
}
mx[0]=num=n;
getroot(1,0);
partition(root);
printf("%lld",ans);
}

BZOJ3697采药人的路径——点分治的更多相关文章

  1. BZOJ3697:采药人的路径(点分治)

    Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材. 采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的. 采药人每天都要进行采药 ...

  2. [bzoj3697]采药人的路径——点分治

    Brief Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材. 采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的. 采药人每天 ...

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

    [BZOJ3697]采药人的路径 Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材.采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是 ...

  4. [bzoj3697]采药人的路径_点分治

    采药人的路径 bzoj-3697 题目大意:给你一个n个节点的树,每条边分为阴性和阳性,求满足条件的链的个数,使得这条链上阴性的边的条数等于阳性的边的条数,且这条链上存在一个节点,这个节点到一个端点的 ...

  5. bzoj千题计划248:bzoj3697: 采药人的路径

    http://www.lydsy.com/JudgeOnline/problem.php?id=3697 点分治 路径0改为路径-1 g[i][0/1] 和 f[i][0/1]分别表示当前子树 和 已 ...

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

    题目 采药人的药田是一个树状结构,每条路径上都种植着同种药材. 采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的. 采药人每天都要进行采药活动.他选择的路径 ...

  7. BZOJ3697: 采药人的路径(点分治)

    Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材.采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的.采药人每天都要进行采药活动 ...

  8. 2019.01.09 bzoj3697: 采药人的路径(点分治)

    传送门 点分治好题. 题意:给出一棵树,边分两种,求满足由两条两种边数相等的路径拼成的路径数. 思路: 考虑将边的种类转化成边权−1-1−1和111,这样就只用考虑由两条权值为000的路径拼成的路径数 ...

  9. BZOJ 3697/3127 采药人的路径 (点分治)

    题目大意: 从前有一棵无向树,树上边权均为$0$或$1$,有一个采药人,他认为如果一条路径上边权为$0$和$1$的边数量相等,那么这条路径阴阳平衡.他想寻找一条合法的采药路径,保证阴阳平衡.然后他发现 ...

随机推荐

  1. 学习CSS布局 - 没有布局

    如果你只想把所有内容都塞进一栏里,那么不用设置任何布局也是OK的. 然而,如果用户把浏览器窗口调整的很大,这时阅读网页会非常难受: 读完每一行之后,你的视觉焦点要从右到左移动一大段距离. 试着调整下浏 ...

  2. 什么是Mixin模式:带实现的协议

    Mixin(织入)模式并不是GOF的<设计模式>归纳中的一种,但是在各种语言以及框架都会发现该模式(或者思想)的一些应用.简单来说,Mixin是带有全部实现或者部分实现的接口,其主要作用是 ...

  3. 利用shell脚本或者php移动某个文件夹下的文件到各自的日期组成的目录下

    背景是这样的:网站一开始访问量比较小,大家就把所有的图片文件上传到一个目录下(比如是/data/images/).后来访问量大了,图片也多了,这样就影响读取效率.所以有个这样的需求,把这些个图片文件移 ...

  4. 使用Windows API进行串口编程

    使用Windows API进行串口编程   串口通信一般分为四大步:打开串口->配置串口->读写串口->关闭串口,还可以在串口上监听读写等事件.1.打开和关闭串口Windows中串口 ...

  5. JaxbUtil转json转XML工具类

    json转换为XML工具类 package com.cxf.value; import org.springframework.util.StringUtils; import javax.xml.b ...

  6. Luogu P1966 火柴排队

    这还是一道比较简单的题目,稍微想一下就可以解决.终于有NOIP难度的题目了 首先我们看那个∑(ai-bi)^2的式子,发现这个的最小值就是排序不等式 所以我们只需要改变第一组火柴的顺序,使它和第二组火 ...

  7. css-文本左右对齐

    1.给目标元素加 display:block;text-align:justify; 2.如果最后一行没充满整行,因为text-align:justify,所以字字之间会有间隔,应给目标元素加伪类,添 ...

  8. Visual Studio2017 数据库架构比较

    一.前言 开发的时候在测试服务器上和线网服务器上面都有我们的数据库,当我们在线网上面修改或者新增一些字段后,线网的数据库也需要更新,这个时候根据表的修改记录,然后在线网上面一个一个增加修改很浪费效率而 ...

  9. Nginx+upstream针对后端服务器容错的运维笔记

    熟练掌握Nginx负载均衡的使用对运维人员来说是极其重要的!下面针对Nignx负载均衡upstream容错机制的使用做一梳理性说明: 一.nginx的upstream容错 1)nginx 判断节点失效 ...

  10. 测试网站页面网速的一个简单Python脚本

    无聊之余,下面分享一个Python小脚本:测试网站页面访问速度 [root@huanqiu ~]# vim pywww.py #!/usr/bin/python # coding: UTF-8 imp ...