zoukankan      html  css  js  c++  java
  • USACO 2018 January Contest, Platinum Problem 2. Cow at Large JZOJ 100130. 鱼池逃脱(贪心+树上数数转换+点分治)

    题目:

    http://usaco.org/index.php?page=viewproblem2&cpid=793

    https://gmoj.net/senior/#main/show/100130

    题解:

    考虑定根时怎么做?

    假设在一个叶子y放了个人,那么这个人会一直往根的方向走,若根的人往y这个方向走,它俩在中点相遇,也就是根的人不可能从中点的子树走出去。

    贪心的想,每次选深度最小的叶子,然后把中点的子树覆盖了,继续选没有被覆盖的叶子。

    或者说,给每个叶子到根的中点打上标记,然后从根往下遍历,看看遇到多少个标记点。

    得到了(O(n^2))的做法。

    对每个根,我们只想知道有多少个标记点,所以需要找到一种巧妙的统计方法。

    注意这些标记点之间一定不成祖先关系。

    对于一个(k)个点的子树,度数和(=2*k-1),这样的话(=sum_{x在子树里}2-r[i]=1),恰好可以统计这个子树一次。

    这些标记点之间不成祖先关系,那么统计它们子树并的(sum2-r[i])就好了。

    (f[x])(x)到最近叶子的距离,这个可以通过多源BFS或树形dp求出。

    (y)(root)时,(x)会被作为子树并的点统计到,当且仅当(dis(x,y)>=f[x])

    那么就可以点分治了:

    点分治后,相当于统计(dep[x]+dep[y]>=f[x])

    (dep[y]>=f[x]-dep[x])

    (f[x]-dep[x])可能很大,但是(dep[y])一定在分治子树大小内,所以可以记一个分治子树大小的桶,再搞个前缀和来查询,还需要容斥减掉来自同一个分治子树的答案。

    Code:

    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    const int N = 70005;
    
    int n, x, y, r[N];
    int fi[N], to[N * 2], nt[N * 2], tot;
    
    void link(int x, int y) {
    	nt[++ tot] = fi[x], to[tot] = y, fi[x] = tot;
    }
    
    int d[N], d0, bz[N], f[N];
    
    void bfs() {
    	fo(i, 1, n) if(r[i] == 1)
    		d[++ d0] = i, bz[i] = 1;
    	for(int i = 1; i <= d0; i ++) {
    		int x = d[i];
    		for(int j = fi[x]; j; j = nt[j]) {
    			int y = to[j];
    			if(!bz[y]) bz[y] = 1, f[y] = f[x] + 1, d[++ d0] = y;
    		}
    	}
    }
    
    int siz[N], mx[N], G;
    
    void fg(int x) {
    	bz[x] = 1;
    	siz[x] = 1; mx[x] = 0;
    	for(int i = fi[x]; i; i = nt[i]) if(!bz[to[i]])
    		fg(to[i]), siz[x] += siz[to[i]], mx[x] = max(mx[x], siz[to[i]]);
    	mx[x] = max(mx[x], siz[0] - siz[x]);
    	if(mx[x] < mx[G]) G = x;
    	bz[x] = 0;
    }
    
    int dep[N];
    
    void dfs(int x) {
    	bz[x] = 1;
    	d[++ d0] = x;
    	for(int i = fi[x]; i; i = nt[i]) if(!bz[to[i]]) {
    		dep[to[i]] = dep[x] + 1;
    		dfs(to[i]);
    	}
    	bz[x] = 0;
    }
    
    ll cnt[N], ans[N];
    
    void calc(int m, int xs) {
    	fo(i, 0, m) cnt[i] = 0;
    	fo(i, 1, d0) {
    		int v = f[d[i]] - dep[d[i]];
    		if(v <= m) cnt[max(0, v)] += 2 - r[d[i]];
    	}
    	fo(i, 1, m) cnt[i] += cnt[i - 1];
    	fo(i, 1, d0) ans[d[i]] += xs * (cnt[dep[d[i]]]);
    }
    
    void dg(int x) {
    	fg(x);
    	d0 = 0; dep[x] = 0; dfs(x);
    	calc(siz[x], 1);
    	bz[x] = 1;
    	for(int i = fi[x]; i; i = nt[i]) if(!bz[to[i]]) {
    		int y = to[i];
    		d0 = 0; dep[y] = 1; dfs(y);
    		calc(siz[y], -1);
    	}
    	for(int i = fi[x]; i; i = nt[i]) if(!bz[to[i]])
    		siz[0] = siz[to[i]], G = 0, fg(to[i]), dg(G);
    }
    
    int main() {
    	freopen("atlarge.in", "r", stdin);
    	freopen("atlarge.out", "w", stdout);
    	scanf("%d", &n);
    	if(n == 1) {
    		pp("1
    "); return 0;
    	}
    	fo(i, 1, n - 1) {
    		scanf("%d %d", &x, &y);
    		link(x, y); link(y, x);
    		r[x] ++; r[y] ++;
    	}
    	bfs();
    	fo(i, 1, n) bz[i] = 0;
    	siz[0] = mx[0] = n, fg(1), dg(G);
    	fo(i, 1, n) pp("%lld
    ", r[i] == 1 ? 1 : ans[i]);
    }
    
    
  • 相关阅读:
    Python练习题 016:猴子吃桃
    Date() 及其 如何验证用户输入的日期是合法的
    关于事件触发的一个小tips
    KVO监听导航栏
    使用CoreText动态下载更换字体
    UICollectionView 的使用
    echarts散点图搭配时间轴
    使用百度echarts画图表的步骤
    canvas画图中drawImage使用
    html5canvas简单画图
  • 原文地址:https://www.cnblogs.com/coldchair/p/12358165.html
Copyright © 2011-2022 走看看