bzoj2815: [ZJOI2012]災難

題面在這裏

題意:

有一個n個點的關係圖,u->v有邊表示u能喫v。
去掉某個點以後會有一些點沒有東西喫,每個點的災難值定義爲如果去掉這個點,會沒有東西喫的點的個數。
求每個點的災難值。
n<=65534

做法:

好妙啊QAQ..

首先這個不是樹很難受,如果是一棵樹,就可以直接計算子樹大小得到答案了。
我們考慮把圖化成一棵樹。
觀察到一個奇妙的性質,一個點u,如果和很多點v1,v2…vn都有邊(代表u能喫v1,v2…vn),那麼它能喫的東西相當於是v1,v2…vn這些點的共同能喫的東西。
於是你想到了什麼!結合一下“樹”的知識。

lca啊(霧)!

假如我們反着建邊,從上往下考慮,如果發現一個點的父親有多個,就把這個點連向它所有父親的lca。一層層往下建就建出了一棵樹。
於是在樹上dfs一遍就得到答案了。

代碼:

/*************************************************************
    Problem: bzoj 2815 [ZJOI2012]災難
    User: fengyuan
    Language: C++
    Result: Accepted
    Time: 212 ms
    Memory: 17452 kb
    Submit_Time: 2018-01-24 19:34:32
*************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cctype>
#include<cstdlib>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;

inline ll read() {
    char ch = getchar(); ll x = 0; int op = 1;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
    for(; isdigit(ch); ch = getchar()) x = x*10+ch-'0';
    return x*op;
}
inline void write(ll a) {
    if(a < 0) putchar('-'), a = -a;
    if(a >= 10) write(a/10); putchar('0'+a%10);
}

const int N = 100000, M = 200010;
int n, cnt, tot;
int head[N], headt[N], f[N][25], depth[N], in[N], q2[N], sz[N], vis[N];
struct edge {
    int to, nxt;
    edge() {}
    edge(int x, int y) { to = x, nxt = y; }
}e[M], t[M];

inline void addedge(int x, int y) { e[++ cnt] = edge(y, head[x]); head[x] = cnt; }
inline void addtree(int x, int y) { t[++ cnt] = edge(y, headt[x]); headt[x] = cnt; }
inline void tp() {
    queue<int> q;
    for(int i = 1; i <= n; i ++) if(!in[i]) q.push(i);
    while(!q.empty()) {
        int u = q.front(); q.pop(); q2[++ tot] = u;
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to; in[v] --;
            if(!in[v]) q.push(v);
        }
    }
}
inline int lca(int x, int y) {
    if(x == -1) return y;
    if(depth[x] < depth[y]) swap(x, y);
    int tmp = depth[x] - depth[y];
    for(int i = 20; i >= 0; i --) if(tmp>>i&1) x = f[x][i];
    if(x == y) return x;
    for(int i = 20; i >= 0; i --)
        if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
    return f[x][0];
}
inline void build() {
    for(int i = tot; i >= 1; i --) {
        int u = q2[i], fa = -1;
        for(int j = head[u]; j; j = e[j].nxt) fa = lca(fa, e[j].to);
        if(fa == -1) fa = 0; addtree(fa, u); vis[u] = 1;
        depth[u] = depth[fa] + 1;
        f[u][0] = fa;
        for(int i = 1; 1<<i <= depth[u]; i ++) f[u][i] = f[f[u][i-1]][i-1];
    }
}
inline void dfs(int u) {
    sz[u] = 1;
    for(int i = headt[u]; i; i = t[i].nxt) {
        int v = t[i].to; dfs(v);
        sz[u] += sz[v];
    }
}
int main() {
    n = read();
    for(int i = 1; i <= n; i ++) {
        int x = read();
        while(x) { addedge(i, x); in[x] ++; x = read(); }
    }
    tp(); build(); dfs(0);
    for(int i = 1; i <= n; i ++) write(sz[i]-1), puts("");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章