BZOJ3697采药人的路径——点分治
题目描述
采药人的药田是一个树状结构,每条路径上都种植着同种药材。
采药人以自己对药材独到的见解,对每种药材进行了分类。大致分为两类,一种是阴性的,一种是阳性的。
采药人每天都要进行采药活动。他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径。采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的。他想知道他一共可以选择多少种不同的路径。
输入
第1行包含一个整数N。
接下来N-1行,每行包含三个整数a_i、b_i和t_i,表示这条路上药材的类型。
输出
输出符合采药人要求的路径数目。
样例输入
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 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采药人的路径——点分治的更多相关文章
- BZOJ3697:采药人的路径(点分治)
Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材. 采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的. 采药人每天都要进行采药 ...
- [bzoj3697]采药人的路径——点分治
Brief Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材. 采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的. 采药人每天 ...
- 【BZOJ3697】采药人的路径 点分治
[BZOJ3697]采药人的路径 Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材.采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是 ...
- [bzoj3697]采药人的路径_点分治
采药人的路径 bzoj-3697 题目大意:给你一个n个节点的树,每条边分为阴性和阳性,求满足条件的链的个数,使得这条链上阴性的边的条数等于阳性的边的条数,且这条链上存在一个节点,这个节点到一个端点的 ...
- bzoj千题计划248:bzoj3697: 采药人的路径
http://www.lydsy.com/JudgeOnline/problem.php?id=3697 点分治 路径0改为路径-1 g[i][0/1] 和 f[i][0/1]分别表示当前子树 和 已 ...
- BZOJ3697 采药人的路径 【点分治】
题目 采药人的药田是一个树状结构,每条路径上都种植着同种药材. 采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的. 采药人每天都要进行采药活动.他选择的路径 ...
- BZOJ3697: 采药人的路径(点分治)
Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材.采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的.采药人每天都要进行采药活动 ...
- 2019.01.09 bzoj3697: 采药人的路径(点分治)
传送门 点分治好题. 题意:给出一棵树,边分两种,求满足由两条两种边数相等的路径拼成的路径数. 思路: 考虑将边的种类转化成边权−1-1−1和111,这样就只用考虑由两条权值为000的路径拼成的路径数 ...
- BZOJ 3697/3127 采药人的路径 (点分治)
题目大意: 从前有一棵无向树,树上边权均为$0$或$1$,有一个采药人,他认为如果一条路径上边权为$0$和$1$的边数量相等,那么这条路径阴阳平衡.他想寻找一条合法的采药路径,保证阴阳平衡.然后他发现 ...
随机推荐
- Omi框架学习之旅 - 插件机制之omi-router及原理说明
先来看看官网的介绍吧:https://github.com/AlloyTeam/omi/tree/master/plugins/omi-router 其实我推荐直接看官网的介绍.我所写的,主要给个人做 ...
- 每个大主播都是满屏弹幕,怎么做到的?Python实战无限刷弹幕!
anmu 是一个开源的直播平台弹幕接口,使用他没什么基础的你也可以轻松的操作各平台弹幕.使用不到三十行代码,你就可以使用Python基于弹幕进一步开发.支持斗鱼.熊猫.战旗.全民.Bilibili多平 ...
- BJOI2019 题解
BJOI2019 题解 在更了在更了 P5319 [BJOI2019]奥术神杖 对\(V_i\)求个\(\ln\)变成了让平均数最大,显然套分数规划,然后ac自动机上面dp #include<b ...
- 值类型和引用类型的区别,struct和class的区别
C#值类型和引用类型 1.简单比较 值类型的变量直接存储数据,而引用类型的变量持有的是数据的引用,数据存储在数据堆中. 值类型(value type):byte,short,int,long,floa ...
- mybatis 思考
https://my.oschina.net/xianggao/blog/548579 https://my.oschina.net/xianggao/blog/548873 https://my.o ...
- linux RPM manager
RPM manage:rpm2cpio package_name | cpio -id #将一个rpm包解压至当前目录rpm -qi package_name #查看一个已安装的rpm包信息rpm - ...
- sql 某字段存储另一个表的多个id值并以逗号分隔,现根据id去中文并拼接同样以逗号分隔
首先介绍用到的两个函数 charindex(要查找的表达式1,表达式2),返回值为表达式1在表达式2中的下标,未找到则返回0.(sql的下标是从1开始的),例如 select charindex('s ...
- ELK基础架构解说-运维笔记
一.ELK日志分析工具介绍1) Elasticsearch1.1) Elasticsearch介绍ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索 ...
- vs2015安装及初步试用
Vs2015一直都听说好用,便捷.之前用vc++6.0,总感觉界面很灰,让人编程兴趣不高,恰巧借此机会,安装一下vs2015,从编译器上体验下编程的舒心,方便.希望我不会变得太懒... 首先,我下的是 ...
- QT下opencv的编译和使用
需要的文件 qt-opensource-windows-x86-mingw491_opengl-5.4.0.exe cmake-3.12.0-rc1-win64-x64.msi opencv-2.4. ...