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

7
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1

Sample Output

1

HINT

对于100%的数据,N ≤ 100,000。

Source

 

[Submit][Status][Discuss]

HOME Back

点分治经典题。但是细节好多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: 采药人的路径的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. BZOJ3697: 采药人的路径

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

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

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

随机推荐

  1. react input 设置默认值

    1.text类型 <input type="text" value={默认值} />  ,这种写法可以显示默认值,但不能对输入框进行编辑 正确写法: <input ...

  2. JS设计模式——1.富有表现力的JS

    创建支持链式调用的类(构造函数+原型) Function.prototype.method = function(name, fn){ this.prototype[name] = fn; retur ...

  3. Windows降权

    使用invoke-tokenmanipulation进行降权 枚举所有令牌 PS C:\Users\SMC> Get-ExecutionPolicy Restricted PS C:\Users ...

  4. redis基础之redis-sentinel(哨兵集群)(六)

    前言 redis简单的主从复制在生产的环境下可能是不行的,因为从服务器只能读不能写,如果主服务器挂掉,那么整个缓存系统不能写入了:redis自带了sentinel(哨兵)机制可以实现高可用. redi ...

  5. Python 生成随机数

    import random x = int(input('Enter a number for x: '))  --随机数最小值y = int(input('Enter a number for y: ...

  6. URL中斜杠/和反斜杠\的区别小结

    Unix使用斜杆/ 作为路径分隔符,而web应用最新使用在Unix系统上面,所以目前所有的网络地址都采用 斜杆/ 作为分隔符. Windows由于使用 斜杆/ 作为DOS命令提示符的参数标志了,为了不 ...

  7. Shell语言系列之一:文件处理

    前言 &nbsp 标准输入/输出可能是软件工具设计原则里最基本的观念了.有很多UNIX程序都遵循这一设计历练.默认情况下,他们会读取标准输入,写入标准输出,并将错误信息传递给标准错误输出. & ...

  8. 转:[译]CSV 注入:被人低估的巨大风险

    转:https://yq.aliyun.com/articles/225847 原文地址:The Absurdly Underestimated Dangers of CSV Injection 原文 ...

  9. BNUOJ 52511 Keep In Line

    队列,$map$. 每次出队进行出队操作的是时候,先把队列中需要出队的人全部出队,然后比较对头和当前出队的人是否相同. #include<bits/stdc++.h> using name ...

  10. Python数据类型-列表(list)增删改查

    1.添加元素 添加单个元素:使用append(object)函数可以为列表添加单个元素,参数object为对象:也就是说所有Python的对象都可以添加到列表中. 添加多个元素(合并列表):使用ext ...