zoukankan      html  css  js  c++  java
  • 【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

    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 = 200005;
    
    int n;
    
    int stot, tov[N*2], nex[N*2], h[N], w[N*2];
    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] = 1;
        int res = 0;
        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][2], f[N][2];
    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]][1] ++;
        else g[dis[u]][0] ++;
        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] + 1;
            dis[v] = dis[u] + w[i];
            dfs ( v, u );
        }
        t[dis[u]] --;
    }
    
    ll ans;
    void work ( int u ) {
        vis[u] = 1; int mx = 0; f[n][0] = 1;
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( vis[v] ) continue;
            dis[v] = n + w[i]; dep[v] = 1;
            maxdep = 1; dfs ( v, 0 ); mx = max ( mx, maxdep );
            ans += 1ll * ( f[n][0] - 1 ) * g[n][0];
            for ( int j = -maxdep; j <= maxdep; j ++ )
                ans += f[n+j][0] * g[n-j][1] + f[n+j][1] * g[n-j][0] + f[n+j][1] * g[n-j][1];
            for ( int j = -maxdep; j <= maxdep; j ++ ) {
                f[n-j][0] += g[n-j][0]; g[n-j][0] = 0;
                f[n-j][1] += g[n-j][1]; g[n-j][1] = 0;
            }
        }
        for ( int i = -mx; i <= mx; i ++ )
            f[n+i][0] = f[n+i][1] = 0;
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( !vis[v] ) {
                asize = 0x3f3f3f3f, size = siz[v]; root = 0;
                findroot ( v, 0 );
                work ( root );
            }
        }
    }
    
    int main ( ) {
        scanf ( "%d", &n );
        for ( int i = 1; i < n; i ++ ) {
            int u, v, s;
            scanf ( "%d%d%d", &u, &v, &s );
            s = s ? 1 : -1;
            add ( u, v, s );
            add ( v, u, s );
        }
        size = n; asize = 0x3f3f3f3f;
        findroot ( 1, 0 );
        work ( root );
        printf ( "%lld", ans );
        return 0;
    }
  • 相关阅读:
    Codeforces Round #665 (Div. 2) C. Mere Array 数论,思维
    Codeforces Round #665 (Div. 2) B
    ZOJ
    HDU-2158 最短区间版大家来找茬 模拟 尺取
    HDU-1082 排列组合 普通生成函数 细节
    HDU
    poj-1651 multiplication puzzle(区间dp)
    hdu-1231 连续最大子序列(动态规划)
    poj-2488 a knight's journey(搜索题)
    hdu-2063 过山车(二分图)
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9513087.html
Copyright © 2011-2022 走看看