题目描述:这里

发现还是点对之间的问题,于是还是上点分

只不过是怎么做的问题

首先对每条边边权给成1和-1(即把原来边权为0的边边权改为-1),那么合法的路径总权值一定为0!

还是将路径分为经过当前根节点和不经过当前根节点的,对不经过当前点的递归处理

那么我们讨论经过当前根节点的路径算法即可

可以发现,如果一条路径经过当前根节点,那么当前根节点可以将这条路径分成两部分,且这两部分权值互为相反数!

(这是很显然的,如果不互为相反数的话那么加起来肯定不是0啊)

接下来我们分析中转站的问题:

其实中转站的含义就是在路径上找到一个点,使得这个点左右两部分权值和均为0

那么这里需要dp处理

我们用两个dp数组处理,分别为$f[i][0/1],g[i][0/1]$,其中g是f的前缀和,f[i][0]表示长度为i的路径,0/1用来记录路径上是否存在距离为i的节点

之所以要记录这一点,是因为起点和终点不能作为休息站,所以当某个权值第一次出现时我们要记录在f[i][0]中,在另半条路径中找到g[-i][1]的点数才能保证休息站的存在

那么答案就由几部分组成:

第一:之前的几棵子树中某一个点到根的路径权值为0,当前子树中某一个点到根的路径权值也为0,那么方案数就是$f[0][0]*g[0][0]$

第二:之前的几棵子树中某一个点到根的路径权值为i,当前子树中某一个点权值为-i,那么答案即为$f[i][1]*g[-i][0]+f[i][0]*g[-i][1]+f[i][1]*g[i][-1]$(注意这里的i不一定是正值!!!)

还是比较好理解,因为不管这个权值之前是否出现过,只需互为相反数然后累计即可,因为左右一定能找到一个合法的位置

但是注意一点:

我们认为根节点的g[0][0]初值为1,这样在累计以根节点为端点的情况的时候是有效的,但对于根节点恰好是端点而且根节点被迫成为休息站的情况是有问题的,这种情况是不合法的,所以事实上累计两边权值都是0的时候应该用的是$f[0][0]*(g[0][0]-1)$!!

然后算完把f累计到g里做个前缀和就可以了

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
struct Edge
{
int next;
int to;
int val;
}edge[];
int head[];
int siz[];
int maxp[];
bool vis[];
int dis[];
int dep[];
int has[];
ll g[][];
ll f[][];
ll ans=;
int maxdep=;
int cnt=;
int s,rt;
int n;
void init()
{
memset(head,-,sizeof(head));
cnt=;
}
void add(int l,int r,int w)
{
edge[cnt].next=head[l];
edge[cnt].to=r;
edge[cnt].val=w;
head[l]=cnt++;
}
void get_rt(int x,int fa)
{
siz[x]=,maxp[x]=;
for(int i=head[x];i!=-;i=edge[i].next)
{
int to=edge[i].to;
if(to==fa||vis[to])continue;
get_rt(to,x);
siz[x]+=siz[to];
maxp[x]=max(maxp[x],siz[to]);
}
maxp[x]=max(maxp[x],s-siz[x]);
if(maxp[x]<maxp[rt])rt=x;
}
void get_dis(int x,int fa)
{
maxdep=max(maxdep,dep[x]);
if(has[dis[x]])f[dis[x]][]++;
else f[dis[x]][]++;
has[dis[x]]++;
for(int i=head[x];i!=-;i=edge[i].next)
{
int to=edge[i].to;
if(vis[to]||to==fa)continue;
dep[to]=dep[x]+;
dis[to]=dis[x]+edge[i].val;
get_dis(to,x);
}
has[dis[x]]--;
}
void solve(int x)
{
vis[x]=;
g[n][]=;
int maxx=;
for(int i=head[x];i!=-;i=edge[i].next)
{
int to=edge[i].to;
if(vis[to])continue;
dis[to]=n+edge[i].val,maxdep=,dep[to]=;
get_dis(to,);
maxx=max(maxx,maxdep);
ans+=(g[n][]-)*f[n][];
for(int j=-maxdep;j<=maxdep;j++)ans+=g[n-j][]*f[n+j][]+g[n-j][]*f[n+j][]+g[n-j][]*f[n+j][];
for(int j=n-maxdep;j<=n+maxdep;j++)
{
g[j][]+=f[j][];
g[j][]+=f[j][];
f[j][]=f[j][]=;
}
}
for(int j=n-maxx;j<=n+maxx;j++)g[j][]=g[j][]=;
for(int i=head[x];i!=-;i=edge[i].next)
{
int to=edge[i].to;
if(vis[to])continue;
rt=,maxp[rt]=inf,s=siz[to];
get_rt(to,);
solve(rt);
}
}
int main()
{
scanf("%d",&n);
init();
for(int i=;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(!z)z--;
add(x,y,z),add(y,x,z);
}
maxp[rt]=s=n;
get_rt(,);
solve(rt);
printf("%lld\n",ans);
return ;
}

bzoj 3697的更多相关文章

  1. [BZOJ 3697] 采药人的路径

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=3697 [算法] 首先 , 将黑色的边变成1 ,白色的边变成-1 那么 , 问题就转化 ...

  2. BZOJ 3697: 采药人的路径 [点分治] [我想上化学课]

    传送门 题意: 路径有$-1,1$两种权值,求有多少路径满足权值和为$0$且有一个点将路径分成权值和为$0$的两段 第四节课本来想去上化学,然后快上课了这道题还没调出来.....可恶我想上化学 昨天两 ...

  3. 【BZOJ 3697】采药人的路径

    题目链接: TP 题解: 调了好久233. 大概想一想就是树分,然后考虑这样路径(u,v)的特征,以根节点(root)切开,u到root的阴阳差值,和v到root巧合互为相反数,然后考虑要有一个点可作 ...

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

    点分治,设当前处理的块的重心为rt,预处理出每个子树中f[v][0/1]表示组合出.没组合出一对值v的链数(从当前儿子出发的链),能组合出一对v值就是可以有一个休息点 然后对于rt,经过rt且合法的路 ...

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

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

  6. BZOJ 3697: 采药人的路径 点分治

    好久不做点分治的题了,正好在联赛之前抓紧复习一下. 先把边权为 $0$ 的置为 $-1$.定义几个状态:$f[dis][0/1],g[dis][0/1]$ 其中 $f$ 代表在当前遍历的子树内的答案. ...

  7. 【BZOJ】【3697】采药人的路径&【3127】【USACO2013 Open】Yin and Yang

    点分治 Orz hzwer 倒是比较好想到点分治……然而在方案统计这里,我犯了两个错误…… 1.我比较傻逼的想的是:通过儿子来更新父亲,也就是统计以x为根的子树中xxxx的路径有多少条……这样转移. ...

  8. BZOJ3697: 采药人的路径

    传送门 不是那么裸的点分治. $f[i][0/1]$表示当前节点的一个子树中总权值和为$i$,且是否存在一个前缀使得其前缀和为$i$ $g[i][0/1]$表示当前节点的已遍历过的子树,其余一样. 对 ...

  9. NOI前总结:点分治

    点分治: 点分治的题目基本一样,都是路径计数. 其复杂度的保证是依靠 $O(n)$ 找重心的,每一次至少将问题规模减小为原先的$1/2$. 找重心我喜欢$BFS$防止爆栈. int Root(int ...

随机推荐

  1. xgboost 非官方每天编译

    xgboost  http://ssl.picnet.com.au/xgboost/ 非官方每天编译

  2. AtCoder瞎做第二弹

    ARC 067 F - Yakiniku Restaurants 题意 \(n\) 家饭店,\(m\) 张餐票,第 \(i\) 家和第 \(i+1\) 家饭店之间的距离是 \(A_i\) ,在第 \( ...

  3. 【地图功能开发系列:二】根据地址名称通过百度地图API查询出坐标

    根据地址名称通过百度地图API查询出坐标 百度地图ApiUrl string url = "http://api.map.baidu.com/geocoder?address={0}& ...

  4. 51nod1237 最大公约数之和

    题目链接 题意 其实就是求 \[\sum\limits_{i=1}^n\sum\limits_{j=1}^ngcd(i,j)\] 思路 建议先看一下此题的一个弱化版 推一下式子 \[\sum\limi ...

  5. 使用django执行数据更新命令时报错:django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependency users.00 01_initial on database 'default'.

    如果在重新封装更新用户表之前,已经更新了数据表,在数据库中已经有了django相关的依赖表,就会报错: django.db.migrations.exceptions.InconsistentMigr ...

  6. Java基础 -- 深入理解Java异常机制

    异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwable类的众多子类描述各种不同的异常. ...

  7. 洛伦兹曲线(Lorenz curve)提升指数、提升表和提升图

    sklearn实战-乳腺癌细胞数据挖掘 https://study.163.com/course/introduction.htm?courseId=1005269003&utm_campai ...

  8. go语言基础知识笔记(二)之数组和切片

    数组和切片知识用的也是比较多,的给我们工作带来很大的便利 (一) 数组 定义:在golang中数组的长度是不可变,数组存放要求是同一种数据类型 //golang中数组定义的四种方法1.先声明,后赋值 ...

  9. [译]Ocelot - Service Discovery

    原文 你可以指定一个service discovery provider,ocelot将使用它来找下游的host和port. Consul 下面的配置要放在GlobalConfiguration中.如 ...

  10. spring 4 + hibernate 4 配置数据库事务

    配置事务时应该加载aopalliance-1.0.jar和aspectjweaver.jar这两个包,这两个包是必须的.