zoukankan      html  css  js  c++  java
  • BZOJ3697 采药人的路径

    Description

    ​ 给定(n)个点的树, 每条边的权值为0/1, 求满足路径上的0与1的个数相等.并且路径上存在一点, 它到起点与终点的路径上0与1的个数相等.求路径条数.

    n <= 100000

    Solution

    一看到这题是统计树上路径的问题, 果断大力点分治.

    考虑没有路径上存在的特殊点怎么做, 我们记边权为0的边的边权为-1, 然后只要统计树上路径长度为0的路径条数即可, 这就是裸的点分治/.

    但这个特殊点的存在就使题目本身变的很毒瘤,考虑加了特殊点怎么搞.

    显然在根节点到某点的路径上, 如果有到端点深度等于端点的深度的点, 那此点可以作为特殊点

    在当前的过分治中心的路径中.我们可以简单分类为三种:以重心为特殊点,特殊点在起点方向,特殊点在终点方向.于是我们像前面那道题目一样,采用前缀和思想:记(f[i][0/1])表示根节点已经遍历过的子树中深度为i,路上有没有深度相同的点.记(g[i][0/1])表示当前处理的子树内的情况, 其余意义与f相同.

    那么第一种答案就为:(f[0][0] * g[0][0])

    关于第二三种, 我们枚举深度:(sum_{i = -Dep}^{Dep} f[i][0] * g[-i][1] + f[i][1] * g[-i][0] + f[i][1] * g[-i][1])

    然后直接统计即可

    Codes

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
    #define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
    #define clar(a, b) memset((a), (b), sizeof(a))
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    #define Debug(s) debug("The massage in line %d, Function %s: %s
    ", __LINE__, __FUNCTION__, s)
    typedef long long LL;
    typedef long double LD;
    const int BUF_SIZE = (int)1e6 + 10;
    struct fastIO {
        char buf[BUF_SIZE], buf1[BUF_SIZE];
        int cur, cur1;
        FILE *in, *out;
        fastIO() {
            cur = BUF_SIZE, in = stdin, out = stdout;
    		cur1 = 0;
        }
        inline char getchar() {
            if(cur == BUF_SIZE) fread(buf, BUF_SIZE, 1, in), cur = 0;
            return *(buf + (cur++));
        }
        inline void putchar(char ch) {
            *(buf1 + (cur1++)) = ch;
            if (cur1 == BUF_SIZE) fwrite(buf1, BUF_SIZE, 1, out), cur1 = 0;
        }
        inline void flush() {
            if (cur1 > 0) fwrite(buf1, cur1, 1, out);
            cur1 = 0;
        }
    }IO;
    #define getchar IO.getchar
    #define putchar IO.putchar
    int read() {
    	char ch = getchar();
    	int x = 0, flag = 1;
    	for(;!isdigit(ch); ch = getchar()) if(ch == '-') flag *= -1;
    	for(;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    	return x * flag;
    }
    void write(LL x) {
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + 48);
    }
    
    #define Maxn 100009
    struct edge {
    	int to, nxt, w;
    }g[Maxn << 1];
    int n, head[Maxn], e;
    void add(int u, int v, int w) {
    	g[++e] = (edge){v, head[u], w}, head[u] = e;
    }
    int size[Maxn], vis[Maxn];
    void dfs_init(int u, int f) {
    	size[u] = 1;
    	for(int i = head[u]; ~i; i = g[i].nxt) {
    		int v = g[i].to;
    		if(v != f && !vis[v]) {
    			dfs_init(v, u);
    			size[u] += size[v];
    		}
    	}
    }
    
    int heart, BEST;
    void centroid(int u, int fa, int field) {
    	int remain = field - size[u];
    	for(int i = head[u]; ~i; i = g[i].nxt) {
    		int v = g[i].to;
    		if(!vis[v] && v != fa) {
    			centroid(v, u, field);
    			remain = max(remain, size[v]);
    		}
    	}
    	if(remain < BEST) BEST = remain, heart = u;
    }
    LL f[Maxn << 1][2], G[Maxn << 1][2], dep[Maxn << 1], vis1[Maxn << 1], Dep[Maxn << 1];
    LL ans, MaxDep;
    void count(int u, int fa) {
    /**/MaxDep = max(MaxDep, Dep[u]);
    	(++vis1[dep[u]] >= 2) ? ++G[dep[u]][1] : ++G[dep[u]][0];
    	for(int i = head[u]; ~i; i = g[i].nxt) {
    		int v = g[i].to;
    		if(!vis[v] && v != fa) {
    			Dep[v] = Dep[u] + 1;
    			dep[v] = dep[u] + g[i].w;
    			count(v, u);
    		}
    	}
    /**/--vis1[dep[u]];
    }
    LL GMax = 0;
    void calc(int u) {
    	f[n][0] = 1; vis1[0] = 1;
    	GMax = 0;
    	for(int i = head[u]; ~i; i = g[i].nxt) {
    		int v = g[i].to;
    		if(!vis[v]) {
    			dep[v] = g[i].w + n; MaxDep = Dep[v] = 1;
    			count(v, u);
    			GMax = max(GMax, MaxDep);
    /**/		ans += (f[n][0] - 1) * G[n][0];
    			rep(j, -MaxDep, MaxDep) 
    				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];
    			rep(j, n - MaxDep, n + MaxDep) {
    				f[j][0] += G[j][0], G[j][0] = 0;
    				f[j][1] += G[j][1], G[j][1] = 0;
    			}
    		}
    	}
    	f[n][0] = 0; vis1[0] = 0;
    	rep(j, n - GMax, n + GMax) 
    		f[j][0] = G[j][0] = f[j][1] = G[j][1] = 0;
    }
    void divide_conquer(int u) {
    	dfs_init(u, 0), BEST = INT_MAX;	
    	centroid(u, 0, size[u]);
    	calc(heart), vis[heart] = 1;
    	for(int i = head[heart]; ~i; i = g[i].nxt) {
    		int v = g[i].to;
    		if(!vis[v]) divide_conquer(v);
    	}
    }
    int main() {
    /**/clar(head, -1);
    	n = read();
    	rep(i, 1, n - 1) {
    		int a = read(), b = read(), c = read() ? 1 : -1;
    		add(a, b, c), add(b, a, c);
    	}
    	divide_conquer(1);
    	cout << ans << endl;
    #ifdef Qrsikno
    	cerr << clock() * 1.0 / CLOCKS_PER_SEC << endl;
    #endif
    	return 0;
    }
    

    Debug

    1.链式前向星忘记初始化(这强调过(N^N)遍辣!)

    2.点分治没有初始化$$g[0][0]$$ = 1,计算时也要相应减去.

    3.清空数组时只要把当前用过的深度清除. 不要全清除, 不然是$$O(n^2)$$的.

  • 相关阅读:
    JAVA 8 主要新特性 ----------------(一)总纲
    STARTTLS is required but host does not support STARTTLS
    Request processing failed; nested exception is java.lang.IllegalStateException: getOutputStream() has already been called for this response
    MyBatis-Plus 多库部署方式;spring mvc 多库部署方式
    Mac could not read from remote repository
    IntelliJ IDEA 2017版 spring-boot2.0.2 搭建 JPA springboot DataSource JPA sort排序方法使用方式, 添加关联表的 order by
    CentOS7 安装可视化脚本安装包Webmin
    VSCode 设置侧边栏字体大小;Visual Studio Code改变侧边栏大小
    java 开发中经常问到得懒汉模式 (单利模式)
    java 中多线程的同步函数的运用
  • 原文地址:https://www.cnblogs.com/qrsikno/p/9791352.html
Copyright © 2011-2022 走看看