Chlience

【题解】BZOJ 2035 [2009国家集训队]数据读取问题
忽然没有了改题的欲望,我已经是一条咸鱼了题目链接:Click here单链将其转化为图论模型对于位置为$x$的点,...
扫描右侧二维码阅读全文
13
2018/10

【题解】BZOJ 2035 [2009国家集训队]数据读取问题

忽然没有了改题的欲望,我已经是一条咸鱼了

题目链接:Click here

单链

将其转化为图论模型

对于位置为$x$的点,向 $x - 1$ , $x + 1$ 连边,表示能到达 $x$ 的点能够花费 $1$ 的代价通过 $+1 / -1$ 而转移到 $x - 1$ , $x + 1$
同时向 $x + a[x] + 1$ 连边,表示能够花费 $1$ 的代价直接连到 $x + a[x] + 1$ 。

同时因为直接转移到 $n$ 的边已经变成了转移到 $n + 1$ ,同时代价 $+1$ , 在统计答案时需要 $-1$ 。
而当 $0 -> 1$ 时需要 $1$ 的费用,所以刚好抵消,答案就是 $dis[1][n]$ 。使用BFS求距离即可

同样,用这种方法提取出 $n$ 条链可以作为 $n^2$ 算法

按照原来单链的做法,向 子树中 $dep[i] = dep[j] + a[j] + 1$ 的点连边
但是发现这样最多会有 $n ^ 2$ 条边,所以需要优化

使用 BFS 序来进行优化
因为每次连的边应该是在 BFS 序列上连续的一段 所以每次访问的点我们可以将其合并,使其指向下一个点,直到指向最右侧为止

这样的话我们访问所有节点应该是 $\alpha (n)*n$ 的复杂度,可以接受
所以访问方法就出来了,其实并不需要真的建边,知道如何走合法就行

算法流程如下:

while(!q.empty()) {
    x = q.front();
    if(!vis[fa[x]]) q.push(fa[x]);
    for(i = son[x])
        if(!vis[i]) q.push(i);
    for(i = L - > R in x)
        if(!vis[bfs[i]]) q.push(bfs[i]);//bfs[i]指第bfs序为i的元素编号
        merge(i , i + 1);
}

那么怎么求 $L$ , $R$ 呢?
利用 DFS 的性质即可求出子树中 $L$ , $R$ 在第 $dep[x] + a[x] + 1$ 层的相对位置,再加上前$dep[x] + a[x]$ 层的节点个数,即为 $L$ , $R$
流程如下:

void DFS(int x) {
    ++ dep_num[dep[x]];
    L[x] = dep_num[dep[x] + a[x] + 1] + 1;
    for(i = son[x]) DFS(i);
    R[x] = dep_num[dep[x] + a[x] + 1];
}
for(int i = 1 ; i <= n ; ++ i) {
    L[i] += dep_num_sum[dep[i] + a[i]];
    R[i] += dep_num_sum[dep[i] + a[i]];
}

详细代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;

struct edge {int t , n;}e[N << 1];
int head[N] , tot;

int fa[N << 1];
int bfs[N << 1] , id[N << 1] , dep[N << 1] , dep_num[N] , L[N] , R[N] , cnt , dep_num_sum[N];
int dis[N << 1] , f[N << 1] , a[N];

int n , rt , qwe;

int read() {
    int ans = 0 , flag = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0') {if(ch == '-') flag = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {ans = ans * 10 + ch - '0'; ch = getchar();}
    return ans * flag;
}
void addedge(int u , int v) {
    ++ tot;
    e[tot].t = v;
    e[tot].n = head[u];
    head[u] = tot;
}
void BFS(int x) {
    memset(bfs , -1 , sizeof(bfs));
    memset(dep , 0 , sizeof(dep));
    dep[x] = 1;
    queue <int> q;
    q.push(x);
    while(!q.empty()) {
        x = q.front(); q.pop();
        bfs[++ cnt] = x;
        f[cnt] = cnt;
        for(int i = head[x] ; i ; i = e[i].n) {
            if(!dep[e[i].t]) {
                q.push(e[i].t);
                dep[e[i].t] = dep[x] + 1;
            }
        }
    }
}
void DFS(int x) {
    ++ dep_num[dep[x]];
    if(x > n) return;
    L[x] = dep_num[dep[x] + a[x] + 1] + 1;
    for(int i = head[x] ; i ; i = e[i].n)
        DFS(e[i].t);
    R[x] = dep_num[dep[x] + a[x] + 1];
}
int find(int x) {return f[x] == x ? f[x] : f[x] = find(f[x]);}
int main() {
    freopen("a.in" , "r" , stdin);
    freopen("a.out" , "w" , stdout);
    qwe = n = read();
    for(int i = 1 ; i <= n ; ++ i) {
        a[i] = read();
        if(a[i] == -1) rt = i;
        int m = read();
        for(int j = 0 ; j < m ; ++ j) {
            int x = read();
            fa[x] = i;
            addedge(i , x);
        }
        if(!m) {fa[++ qwe] = i; addedge(i , qwe);}
    }
    BFS(rt); DFS(rt);
    f[cnt + 1] = cnt + 1;
    for(int i = 1 ; i <= n ; ++ i)
        dep_num_sum[i] = dep_num_sum[i - 1] + dep_num[i];
    for(int i = 1 ; i <= n ; ++ i) {
        L[i] += dep_num_sum[dep[i] + a[i]];
        R[i] += dep_num_sum[dep[i] + a[i]];
    }
    queue <int> q;
    for(int i = head[rt] ; i ; i = e[i].n) {q.push(e[i].t); dis[e[i].t] = 1;}
    while(!q.empty()) {
        int x = q.front(); q.pop();
        if(fa[x] != rt) {
            if(!dis[fa[x]]) {
                dis[fa[x]] = dis[x] + 1;
                q.push(fa[x]);
            }
            for(int i = head[x] ; i ; i = e[i].n) {
                if(!dis[e[i].t]) {
                    dis[e[i].t] = dis[x] + 1;
                    if(x <= n) q.push(e[i].t);
                }
            }
        } 
        for(int i = find(L[x]) ; i <= R[x] ; i = find(i)) {
            if(!dis[bfs[i]]) {
                dis[bfs[i]] = dis[x] + 1;
                if(bfs[i] <= n) q.push(bfs[i]);
            }
            f[i] = i + 1;
        }
    }
    sort(dis + n + 1 , dis + cnt + 1);
    for(int i = n + 1 ; i <= cnt ; ++ i)
        printf("%d\n" , dis[i] - 1);
    return 0;
}
Last modification:October 13th, 2018 at 10:47 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment