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$的边数量相等,那么这条路径阴阳平衡.他想寻找一条合法的采药路径,保证阴阳平衡.然后他发现 ...
随机推荐
- 动手动脑(lesson 8)
一. 上面程序在不注释第一个i/j会出错,这是因为程序会顺序运行,在运行到try之前就已经出错,因此不会跳到异常处理. 异常处理基础知识: 二. 三. 运行结果: 运行结果: 四. 运行结果: 总结: ...
- OSGeo.OGR.Geometry
#region 程序集 ogr_csharp.dll, v2.0.50727 // D:\KM行业需求\C++\gdal17_cSharp\ogr_csharp.dll #endregion usin ...
- git使用备注
git clone 代码库地址 git branch -r 查看远程分支 git branch 查看本地分支 git branch -a 查看远程和本地分支.带*的表示正在所处分支. git bra ...
- Luogu3825 NOI2017 游戏 2-SAT
传送门 第一眼看上去似乎是一个3-SAT问题 然而\(d \leq 8\)给我们的信息就是:暴力枚举 枚举\(x\)型地图变成\(a\)型地图还是\(b\)型地图(实际上不要枚举\(c\),因为\(a ...
- Ionic App之国际化(1)单个参数的处理
最近的app开发中需要考虑多语言国际化的问题,经查资料,目前大部分使用的是angular-translate.js这个组件,网站说明是这个:https://angular-translate.gith ...
- Python高级特性(切片,迭代,列表生成式,生成器,迭代器)
掌握了Python的数据类型.语句和函数,基本上就可以编写出很多有用的程序了. 比如构造一个1, 3, 5, 7, ..., 99的列表,可以通过循环实现: L = [] n = 1 while n ...
- JXOI2018简要题解
JXOI2018简要题解 T1 排序问题 题意 九条可怜是一个热爱思考的女孩子. 九条可怜最近正在研究各种排序的性质,她发现了一种很有趣的排序方法: Gobo sort ! Gobo sort 的算法 ...
- UWP简单示例(三):快速开发2D游戏引擎
准备 IDE:Visual Studio 图形 API:Win2D MSDN 教程:UWP游戏开发 游戏开发涉及哪些技术? 游戏开发是一门复杂的艺术,编码方面你需要考虑图形.输入和网络 以及相对独立的 ...
- WPF没落了吗?
从08年开始一直到现在,碰到所有的项目,我个人经手的,都用wpf开发. wpf应该说一直没有火过,一直平平淡淡. 个人为什么一直执着用wpf,开始使用是因公司项目,做了两年wpf开发,后来换工作一直搜 ...
- Promise 原理
异步:可同时好几件事,互不影响: 同步:按循序一件一件.... 异步好多缺点:.... promise就是解决异步计算的这些缺点的,主要用于: 1.异步计算: 2.可以将异步操作队列化 按期望的顺序 ...