bzoj3697: 采药人的路径

来源:互联网 发布:收购博客玩家数据 编辑:程序博客网 时间:2024/06/13 09:57

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=3697

题解

  这是个思路题,对我这样的zz来说可能已经接近自己想出来的极限了。
  一看统计符合条件的路径条数,肯定是点分治,而且肯定是静态的。
  首先把边权变成11,那么一条路径阴阳平衡也就是说它的权值和等于0
  根据点分治的过程,可知重心和路径是一对多的关系,而且一条路径只会对应一个重心,就是说每条路径都只会在搞某一个重心时被统计,而且仅会被统计一次。
  考虑某条路径被统计的时候的情形。
  它肯定会被分成两段,假设这两段是xy,因为权值和为0,所以这两段的权值和一定是相反数。
  题目要求必须找到一个断点,使得断开后两段的权值和都为0
  考虑断点的位置,要么在x段上,要么在y段上,要么就是重心。
  为了不重复统计,我们先统计在x端上或在y段上的,最后统计在重心上的,所以我们必须强制规定重心不在这两段的任何一段上。
  用一个good数组记录某条路径能否在其末端找出一段和等于0,这个可以开个权值桶然后dfs过程中算出。用f[0/1][i]表示good是否具有good属性,权值和为i的路径的条数,那么在一棵子树一棵子树合并的时候,如果当前点now具有good属性,就把f[0][dist[now]]+f[1][dst[now]]计入答案;如果当前点不具有good属性,就把f[1][dist[now]]计入答案。
  以上只是算了断点不在重心的。
  最后再加上断点在重心的,这个可以加入一棵子树的时候搞,如果当前点不具有good属性,就把f[0][0]计入答案。
  打完之后交上去发现WrongAnswer了,写了个对拍,发现还存在一个端点在重心,另一个端点在子树中的情况没计入。这个好说只要做完一个重心之后把f[1][0]计入答案就好了。

代码

//点分治#include <cstdio>#include <algorithm>#define maxn 200010#define ll long long#define forp for(ll p=head[pos];p;p=nex[p])if(to[p]^pre and !grey[to[p]])using namespace std;ll dist[maxn], deep[maxn], N, size[maxn], G, head[maxn], to[maxn], w[maxn],    nex[maxn], tot, list[maxn], cnt[maxn], f[2][maxn];bool grey[maxn], good[maxn];ll ans, sumG;inline void adde(ll a, ll b, ll v){to[++tot]=b;w[tot]=v;nex[tot]=head[a];head[a]=tot;}inline ll read(ll x=0){    char c=getchar();    while(c<48 or c>57)c=getchar();    while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48, c=getchar();    return x;}ll dfs(ll pos, ll pre){    list[++*list]=pos;    size[pos]=1;    good[pos]=(bool)cnt[-dist[pos]+N];    cnt[-dist[pos]+N]++;    forp dist[to[p]]=dist[pos]+w[p], deep[to[p]]=deep[pos]+1,         size[pos]+=dfs(to[p],pos);    cnt[-dist[pos]+N]--;    return size[pos];}void findG(ll pos, ll pre, ll sum){    if(sum<sumG)G=pos, sumG=sum;    forp findG(to[p],pos,sum+*size-(size[to[p]]<<1));}void solve(ll pos){    ll i, p, t;    *list=0, deep[pos]=dist[pos]=0, *size=dfs(pos,-1);    for(i=1,sumG=0;i<=*list;i++)sumG+=deep[list[i]];    findG(G=pos,-1,sumG);    grey[G]=1;    for(p=head[G];p;p=nex[p])        if(!grey[to[p]])        {            *list=0, dist[to[p]]=w[p], dfs(to[p],G);            for(i=1;i<=*list;i++)            {                if(good[list[i]])ans+=f[0][-dist[list[i]]+N]+f[1][-dist[list[i]]+N];                else ans+=f[1][-dist[list[i]]+N];                if(!good[list[i]] and !dist[list[i]])ans+=f[0][N];            }            for(i=1;i<=*list;i++)f[good[list[i]]][dist[list[i]]+N]++;        }    ans+=f[1][N];    for(p=head[G];p;p=nex[p])        if(!grey[to[p]])        {            *list=0, dist[to[p]]=w[p], dfs(to[p],G);            for(i=1;i<=*list;i++)f[good[list[i]]][dist[list[i]]+N]--;        }    for(p=head[G];p;p=nex[p])if(!grey[to[p]])solve(to[p]);}void init(){    ll a, b, v, i;    N=read();    for(i=1;i<N;i++)a=read(), b=read(), v=read()?1:-1, adde(a,b,v), adde(b,a,v);}int main(){    init();    solve(1);    printf("%lld",ans);    return 0;}
0 0
原创粉丝点击