【BZOJ】3697: 采药人的路径
3697: 采药人的路径
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 1718 Solved: 602
[Submit][Status][Discuss]
Description
采药人的药田是一个树状结构,每条路径上都种植着同种药材。
采药人以自己对药材独到的见解,对每种药材进行了分类。大致分为两类,一种是阴性的,一种是阳性的。
采药人每天都要进行采药活动。他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径。采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的。他想知道他一共可以选择多少种不同的路径。
Input
第1行包含一个整数N。
接下来N-1行,每行包含三个整数a_i、b_i和t_i,表示这条路上药材的类型。
Output
输出符合采药人要求的路径数目。
Sample Input
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1
Sample Output
HINT
对于100%的数据,N ≤ 100,000。
Source
点分治经典题。但是细节好多aaa!!
在计算$u$的贡献,即经过$u$的路径方案数时,记录两个数组,$f[dis][0/1]$和$g[dis][0/1]$,$f$表示这棵子树之前所有计算过的子树所作出的贡献。$g$表示当前计算的子树作出的贡献,在$dfs$中更新$g$,用完过后清零更新$f$。$0/1$表示这条到重心的路径上是否有休息站。在$dfs$中开一个桶,记录之前经过过的路径长度。每次回溯回去的时候释放桶。如果当前的$dis$之前就被装进桶中过,就表示从那时到现在的这条路径的$dis$为0,表示这条路径上肯定有一个休息站,更新$g[dis][1]$,否则更新$g[dis][0]$。
$ans$会从所有$f[i][0]*g[-i][1]+f[i][1]*g[-i][0]+f[i][1]*g[-i][1]$转移过来,表示至少有一个休息站和两个休息站的情况。注意,总的还要加上$g[0][0]*(f[0][0]-1)$,表示当前重心为休息站时的贡献,【注意】$f$减一是必须的,因为之前所有子树中计算的$dis$为0的路径还包括了当前重心这一个点,不能计算进去。而要计算从这个点延伸出去的合法链则一定会在后面的$f[0][0]*g[dis][1]$中被计算到,因为这条链如果满足条件,在这条链中的某一个点一定是休息站。
【注意】答案要存为long long
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std; const int N = ; int n; int stot, tov[N*], nex[N*], h[N], w[N*];
void add ( int u, int v, int s ) {
tov[++stot] = v;
w[stot] = s;
nex[stot] = h[u];
h[u] = stot;
} int siz[N], vis[N], size, asize, root;
void findroot ( int u, int f ) {
siz[u] = ;
int res = ;
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( vis[v] || v == f ) continue;
findroot ( v, u );
siz[u] += siz[v];
res = max ( res, siz[v] );
}
res = max ( res, size - siz[u] );
if ( res < asize ) {
asize = res; root = u;
}
} ll g[N][], f[N][];
int t[N], dis[N], maxdep, dep[N];
void dfs ( int u, int f ) {
maxdep = max ( dep[u], maxdep );
if ( t[dis[u]] ) g[dis[u]][] ++;
else g[dis[u]][] ++;
t[dis[u]] ++;
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( v == f || vis[v] ) continue;
dep[v] = dep[u] + ;
dis[v] = dis[u] + w[i];
dfs ( v, u );
}
t[dis[u]] --;
} ll ans;
void work ( int u ) {
vis[u] = ; int mx = ; f[n][] = ;
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( vis[v] ) continue;
dis[v] = n + w[i]; dep[v] = ;
maxdep = ; dfs ( v, ); mx = max ( mx, maxdep );
ans += 1ll * ( f[n][] - ) * g[n][];
for ( int j = -maxdep; j <= maxdep; j ++ )
ans += f[n+j][] * g[n-j][] + f[n+j][] * g[n-j][] + f[n+j][] * g[n-j][];
for ( int j = -maxdep; j <= maxdep; j ++ ) {
f[n-j][] += g[n-j][]; g[n-j][] = ;
f[n-j][] += g[n-j][]; g[n-j][] = ;
}
}
for ( int i = -mx; i <= mx; i ++ )
f[n+i][] = f[n+i][] = ;
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( !vis[v] ) {
asize = 0x3f3f3f3f, size = siz[v]; root = ;
findroot ( v, );
work ( root );
}
}
} int main ( ) {
scanf ( "%d", &n );
for ( int i = ; i < n; i ++ ) {
int u, v, s;
scanf ( "%d%d%d", &u, &v, &s );
s = s ? : -;
add ( u, v, s );
add ( v, u, s );
}
size = n; asize = 0x3f3f3f3f;
findroot ( , );
work ( root );
printf ( "%lld", ans );
return ;
}
【BZOJ】3697: 采药人的路径的更多相关文章
- [BZOJ 3697] 采药人的路径
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=3697 [算法] 首先 , 将黑色的边变成1 ,白色的边变成-1 那么 , 问题就转化 ...
- BZOJ 3697: 采药人的路径 [点分治] [我想上化学课]
传送门 题意: 路径有$-1,1$两种权值,求有多少路径满足权值和为$0$且有一个点将路径分成权值和为$0$的两段 第四节课本来想去上化学,然后快上课了这道题还没调出来.....可恶我想上化学 昨天两 ...
- bzoj 3697: 采药人的路径【点分治】
点分治,设当前处理的块的重心为rt,预处理出每个子树中f[v][0/1]表示组合出.没组合出一对值v的链数(从当前儿子出发的链),能组合出一对v值就是可以有一个休息点 然后对于rt,经过rt且合法的路 ...
- BZOJ 3697: 采药人的路径 点分治
好久不做点分治的题了,正好在联赛之前抓紧复习一下. 先把边权为 $0$ 的置为 $-1$.定义几个状态:$f[dis][0/1],g[dis][0/1]$ 其中 $f$ 代表在当前遍历的子树内的答案. ...
- 【BZOJ】【3697】采药人的路径&【3127】【USACO2013 Open】Yin and Yang
点分治 Orz hzwer 倒是比较好想到点分治……然而在方案统计这里,我犯了两个错误…… 1.我比较傻逼的想的是:通过儿子来更新父亲,也就是统计以x为根的子树中xxxx的路径有多少条……这样转移. ...
- bzoj千题计划248:bzoj3697: 采药人的路径
http://www.lydsy.com/JudgeOnline/problem.php?id=3697 点分治 路径0改为路径-1 g[i][0/1] 和 f[i][0/1]分别表示当前子树 和 已 ...
- 【BZOJ-3697&3127】采药人的路径&YinandYang 点分治 + 乱搞
3697: 采药人的路径 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 681 Solved: 246[Submit][Status][Discus ...
- BZOJ3697: 采药人的路径
传送门 不是那么裸的点分治. $f[i][0/1]$表示当前节点的一个子树中总权值和为$i$,且是否存在一个前缀使得其前缀和为$i$ $g[i][0/1]$表示当前节点的已遍历过的子树,其余一样. 对 ...
- BZOJ_3697_采药人的路径_点分治
BZOJ_3697_采药人的路径_点分治 Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材. 采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性 ...
随机推荐
- Opencv 配置VS2012
开始接触图像处理有一段时间了,经过前期的调研,和相关入门知识的学习,开始接触一些图像处理应用的工具.Opencv是一个图像处理的开源库,由于其开放的协议架构,国内外很多科研机构和团队都在基于openc ...
- 在字符串S1中删除字符串S2中所包含的字符【转】
转自:http://www.cnblogs.com/tolimit/p/4202959.html /************************************************** ...
- elk系列4之kibana图形化操作【转】
preface 我们都搭建了ELK系统,且日志也能够正常收集的时候,那么就配置下kibana.我们可以通过kibana配置柱状图,趋势图,统计图,圆饼图等等各类图.下面就拿配置统计图和柱状图为例,结合 ...
- Ural Sport Programming Championship 2015
Ural Sport Programming Championship 2015 A - The First Day at School 题目描述:给出课程安排,打印一个课程表. solution 暴 ...
- C 中级 - SO_REUSEPORT 和 SO_REUSEADDR
引言 - 问题由来 刚开始学习网络编程时候, 常听到一个词, 先开启 "端口复用 SO_REUSEADDR". 那时很一知半解, 就知道该那么写了. 心里一直有些奇怪, 语义不通呀 ...
- opencv配置过程 (cmake,vs2013,qt 5.4)
平台及软件: Windows 7 X86 Visual Studio 2013 OpenCV3.0.0 Cmake3.3 1.下载Windows下的安装文件OpenCV-3.0.0.exe,解压,选择 ...
- 千万不要运行的 Linux 命令
本文中列出的命令绝对不可以运行,即使你觉得很好奇也不行,除非你是在虚拟机上运行(出现问题你可以还原),因为它们会实实在在的破坏你的系统.所以不在root等高级管理权限下执行命令是很好的习惯. 本文的目 ...
- 不需要打密码的sudo方法
Linux下频繁输入sudo很麻烦.如果你的账户已经是sudoer了,那么编辑/etc/sudoers,将 %sudo ALL=(ALL:ALL) ALL 修改为: %sudo ALL=(ALL) N ...
- DOM的查找与操作
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...
- awk书上练习
文件car: plym fury chevy malibu ford mustang volvo s80 ford thundbd chevy malibu bmw 325i honda accord ...