采药人的路径 bzoj-3697

    题目大意:给你一个n个节点的树,每条边分为阴性和阳性,求满足条件的链的个数,使得这条链上阴性的边的条数等于阳性的边的条数,且这条链上存在一个节点,这个节点到一个端点的链也是阴阳相等,到另一个顶点也是阴阳相等。
    注释:$1\le n \le 10^5$。

      想法:点分治裸题,开始净想着怎么在一条阴阳相等的链上找点了。其实可以在找初步满足条件的链是就直接处理出完全满足条件者。首先,我们先令阳性为1,阴性为-1,求边权和为0即为阴阳相等。紧接着,我们定义

        f[i][1]表示root的子树中满足到重心的链且边权和为i、存在一个点使得该点到这条链上到不是重心的另一个节点的边权和为0的条数。

        f[i][0]同理,只是不存在这样的点。

        g[i][1/0]表示边权和为-i。

      特别地,因为所有的边权都是1 or -1,所以我可以在dfs链的时候直接判断出是否存在这样的节点。具体地:这条链的边权是x,如果这个节点相对于重心是根节点来讲存在一个祖先,使得这个祖先到根节点的边权和也是x,那么显然就存在这样的节点,就是那个祖先。然后,我们可以用l和r分别表示这个点之前祖先的边权和所能达到的下界和上界。因为边权是1 or -1,所以$[l,r]$之间的所有值都是可以取到的。这就处理出了f和g数组。

      剩下的,就是运用单步容斥和简单的点分治模板套用,注意size的问题(抄的GXZlegend的代码,所以就没特意更改size的错误)。

    最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int main()
{
}#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;
int head[N],to[N<<1],val[N<<1],next[N<<1],cnt,vis[N],si[N],ms[N],sum,root,md;
long long f[N][2],g[N][2],ans;
inline void add(int x,int y,int z)
{
to[++cnt]=y,val[cnt]=z,next[cnt]=head[x],head[x]=cnt;
}
void getroot(int x,int fa)
{
si[x]=1,ms[x]=0;
for(int i=head[x];i;i=next[i])
if(!vis[to[i]]&&to[i]!=fa)
getroot(to[i],x),si[x]+=si[to[i]],ms[x]=max(ms[x],si[to[i]]);
ms[x]=max(ms[x],sum-si[x]);
if(ms[x]<ms[root]) root=x;
}
void calc(int x,int fa,int now,int cnt)
{
if(now==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,now+2*val[i]-1,cnt);
}
void dfs(int x,int fa,int now,int l,int r)
{
if(now>=l&&now<=r)
{
if(now>=0)f[now][1]++;
else g[-now][1]++;
}
else
{
if(now>=0)f[now][0]++;
else g[-now][0]++;
}
l=min(l,now),r=max(r,now),md=max(md,max(-l,r));
for(int i=head[x];i;i=next[i])
if(!vis[to[i]]&&to[i]!=fa)
dfs(to[i],x,now+val[i]*2-1,l,r);
}
void solve(int x)
{
vis[x]=1,md=0,calc(x,0,0,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<=md;i++)ans+=f[i][0]*g[i][1]+f[i][1]*g[i][0]+f[i][1]*g[i][1],f[i][0]=f[i][1]=g[i][0]=g[i][1]=0;
for(int i=head[x];i;i=next[i])
{
if(!vis[to[i]])
{
md=0,dfs(to[i],0,2*val[i]-1,0,0),ans-=f[0][1]*(f[0][1]-1)/2,f[0][0]=f[0][1]=0;
for(int j=0;j<=md;j++)ans-=f[j][0]*g[j][1]+f[j][1]*g[j][0]+f[j][1]*g[j][1],f[j][0]=f[j][1]=g[j][0]=g[j][1]=0;
sum=si[to[i]],root=0,getroot(to[i],0),solve(root);
}
}
}
int main()
{
int n,x,y,z;
scanf("%d",&n);
for(int i=1;i<n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
sum=n;
root=0;
ms[0]=n;
getroot(1,0);
solve(root);
printf("%lld\n",ans);
return 0;
}

    小结:在更新l和r时now的值并没有进行更改,所以不需要特殊处理,也就是当前的calc的now是可以直接拿来用的。

[bzoj3697]采药人的路径_点分治的更多相关文章

  1. BZOJ_3697_采药人的路径_点分治

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

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

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

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

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

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

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

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

    3697: 采药人的路径 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 681  Solved: 246[Submit][Status][Discus ...

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

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

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

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

  8. BZOJ3697采药人的路径——点分治

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

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

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

随机推荐

  1. PCB CAM自动化程序协同业务流

    最近IBM在做预审阶段的参数信息提取相关的工作,关于Gerber中的图形方面的信息数据,需由工程IT提供异步Web接口,供IBM创建任务,待Gerber图形信息分析完成后再结果将数据返回给IBM;这里 ...

  2. PCB genesis连孔加除毛刺孔(圆孔与圆孔)实现方法(一)

    一.为什么 连孔加除毛刺孔 原因是 PCB板材中含有玻璃纤维, 毛刺产生位置在于2个孔相交位置,由于此处钻刀受力不均导致纤维切削不断形成毛刺 ,为了解决这个问题:在钻完2个连孔后,在相交处再钻一个孔, ...

  3. PCB Genesis 外形加内角孔实现方法

    在PCB工程制作CAM时,经常会遇到外形拐角处直角的,而客户对内角是要求,比如最大内角要求R0.5mm或者不接受内角, 但成型方式为铣方式,又不是啤板成型,那怎么处理才可以达到要求效果呢,在这里介绍2 ...

  4. python自动化测试学习笔记-4内置函数,处理json

    函数.全局变量 写代码时注意的几点事项: 1.一般写代码的时候尽量少用或不用全局变量,首先全局变量不安全,大家协作的情况下,代码公用容易被篡改,其次全局变量会一直占用系统内容. 2.函数里如果有多个r ...

  5. Jenkins-SVN + Maven + Docker

    第1步:安装插件 Subversion Plug-inMaven Integration pluginCloudBees Docker Build and Publish pluginDeploy t ...

  6. scala的Class

    先看类的定义: package com.test.scala.test import scala.beans.BeanProperty /** * scala 的类 */ //定义一个scala的类 ...

  7. Redis 的简单运算

    Redis 的简单运算 命令 说明 备注 incr key 在原字段上加 1 只能对整数操作 incrby key increment 在原字段上加上整数 (increment) 只能对整数操作 de ...

  8. CF126B Password

    思路: kmp略作修改. 实现: #include <iostream> #include <cstdio> using namespace std; ; int neXt[M ...

  9. 线性回归的Cost function实现

    此处使用Octave来实现 线性方程的代价函数: 代价函数: X 是测试值,假设用矩阵表示为         为了方便用矩阵计算我们把X加一列 1 :                 同时  那么h( ...

  10. Android Unable to add window -- token android.os.BinderProxy@3a067204 is not valid错误分析记录

    打开APP时,出现闪退的情况,查看android studio报错信息,主要为: Unable to add window -- token android.os.BinderProxy@3a0672 ...