zoukankan      html  css  js  c++  java
  • 「BZOJ3697」「FJ2014集训」采药人的路径

    3697: 采药人的路径

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 1723  Solved: 603
    [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

    今天做了昨天的一道点分治的题 细节真的可以说是很多了...

    看到这道题 树上路径 然后就可以想到点分治 然后这道题就是根据乘法原理组合路径求答案的

    然后对于阳性药他的权值是1 然后阴性的是-1 所以说一条阴阳平衡的路径路径和为0  

    由题可知休息站到终点和起点的路径和为0 那么很显然整条路径也一定是合法的 

    所以我们就只需要找到合法的路径就可以了 

    以下题解摘自 http://hzwer.com/4526.html

    路径上的休息站一定是在起点到根的路径上,或者根到终点的路径上。

    如何判断一条从根出发的路径是否包含休息站?只要在dfs中记录下这条路径的和x,同时用个标志数组判断这条路径是否存在前缀和为x的节点。

    这样我们枚举根节点的每个子树。用g[i][0 / 1],f[i][0 / 1]分别表示前面几个子树以及当前子树和为i的路径数目,

    0和1用于区分路径上是否存在前缀和为i的节点。

    那么当前子树的贡献就是$f[0][0] * g[0][0] + sum (f [i][0] * g [-i][1] + f[i][1] * g[-i][0] + f[i][1] * g[-i][1])$

    其中i的范围[-d,d],d为当前子树的深度。 所以就直接给所有值都加上n就可以了 这样子下标就不会超界

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int oo = 1e7;
    const int N = 1e5 + 5;
    int n,head[N],tov[2 * N],nex[2 * N],val[2 * N],sum;
    int tot,size[N],root,mxdeep,MX,deep[N],dis[N],t[N];
    ll f[2 * N][2],g[2 * N][2],ans;
    bool vis[2 * N];
    
    void add(int u,int v,int w) {
        
        tot ++;
        tov[tot] = v;
        nex[tot] = head[u];
        val[tot] = w;
        head[u] = tot;
    }
    
    void find_root(int u,int fa) {
        
        size[u] = 1;
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(v == fa || vis[v]) continue;
            find_root(v,u);
            size[u] += size[v];
        }
        int cmp = max(sum - size[u],size[u]);
        if(cmp < MX) {
            MX = cmp;
            root = u;
        }
    }
    
    void dfs(int u,int fa,int dd) {
        
        mxdeep = max(deep[u],mxdeep);
        dis[u] = dd;
        if(t[dis[u]]) f[dis[u]][1] ++;
        else f[dis[u]][0] ++;
        t[dis[u]] ++;
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(vis[v] || v == fa) continue;
            deep[v] = deep[u] + 1;
            dfs(v,u,dd + val[i]);
        }
        t[dis[u]] -- ;
    } 
    
    void divide(int u) {
        
        vis[u] = true; g[n][0] = 1;
        deep[u] = 0; int mx = 0;
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(vis[v]) continue;
            deep[v] = 1;
            mxdeep = 1;
            dfs(v,u,n + val[i]); 
            mx = max(mx,mxdeep);
            ans += (g[n][0] - 1) * f[n][0];
            for(int j = -mxdeep;j <= mxdeep;j ++) {
                ans += g[n - j][1] * f[n + j][0] + g[n - j][0] * f[n + j][1] + g[n - j][1] * f[n + j][1];
            }    
            for(int j = -mxdeep;j <= mxdeep;j ++) {
                g[n - j][0] += f[n - j][0];
                g[n - j][1] += f[n - j][1];
                f[n - j][0] = f[n - j][1] = 0;
            }
        }
        for(int j = -mx;j <= mx;j ++) {
                g[n - j][0] = g[n - j][1] = 0;
        }
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(vis[v]) continue;
            sum = size[v]; MX = oo;
            find_root(v,u);
            divide(root);
        }
    }
    
    int main( ) {
        
        scanf("%d",& n);
        for(int i = 1;i < n;i ++) {
            int u,v,w;
            scanf("%d%d%d",& u,& v,& w);
            if(! w) w = -1;
            add(u,v,w);  add(v,u,w);
        }
        MX = oo; sum = n;
        find_root(1,0);
        divide(root);
        printf("%lld",ans);
    }
  • 相关阅读:
    Pyhton 单行、多行注释方法
    laravel中不使用 remember_token时退出报错,如何解决?
    PHP实现打印出库单,有没有实现过?
    是不等号的意思
    PHP如何输出合并单元格的表
    一起谈.NET技术,.Net创建Excel文件(插入数据、修改格式、生成图表)的方法 狼人:
    一起谈.NET技术,ASP.NET MVC 通过 FileResult 向浏览器发送文件 狼人:
    一起谈.NET技术,asp.net Ajax AutoComplete控件使用 狼人:
    一起谈.NET技术,Silverlight 拖动复制控件 狼人:
    一起谈.NET技术,ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline[上篇] 狼人:
  • 原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9513967.html
Copyright © 2011-2022 走看看