Noip2016 天天爱跑步【LCA】【差分】

从题目部分分的提示中可以获得很多想法,比如说第六个点树退化成一条链,第九个点所有的s=1,第15个点所有的t=1
然后思考一下,每一条最短路都是一条链,LCA必定存在于每一条链上,每条链可以分为
s->lca和lca->t两部分
设deep[i]为i点的深度,dis为S到T最短路
先假设某个w[i]可以看到玩家,那么w[i]满足的等式是

{deep[S]=w[i]+deep[i]deep[T]deep[i]=disw[i]w[i](Slca)w[i](lcaT) { d e e p [ S ] = w [ i ] + d e e p [ i ] w [ i ] ∈ ( S → l c a ) d e e p [ T ] − d e e p [ i ] = d i s − w [ i ] w [ i ] ∈ ( l c a → T )

然后这道题就是对于每一个玩家,统计有多少个满足条件的w[i]就好了,需要用桶的思想去解决,网上有很多题解,这里就不赘述了(这才是这道题的最难点)

但是会统计到不在S到T最短路上的点,删除方式是在进入i的子树之前算算有多少个满足的,退出i的子树后算算有多少个满足的,用新的减去旧的,多出来的就是i的子树中的点

#include 
#include 
#include 
#include 
#include  
#include 
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 300010*2;
const int MAXM = 300010;
int n,m,tot,last[MAXN],vis[MAXN],depth[MAXN],f[MAXN][25],mt;
int maxdepth,w[MAXN],barrel[MAXN],st[MAXN],ans[MAXN];
vector <int> dif1[MAXN], dif2[MAXN], dif3[MAXN];
struct Pla{
    int s,t,lca,len;
}pla[MAXN];
inline void r(int &x) {
    x = 0;
    char ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >='0' && ch <='9') x = x*10+ch-'0', ch = getchar();
    return;
}
struct Edge{
    int u,v,to;
    Edge(){}
    Edge(int u, int v, int to) : u(u), v(v), to(to) {}
}e[MAXM*2];
inline void add(int u, int v) {
    e[++tot] = Edge(u,v,last[u]);
    last[u] = tot;
}

void init_dfs(int x) {
    for(int i=last[x]; i; i=e[i].to) {
        int v = e[i].v;
        if(depth[v]) continue;
        depth[v] = depth[x] + 1;
        maxdepth = max(maxdepth,depth[v]);
        f[v][0] = x;
        init_dfs(v);
    }
}

void init_lca() {
    mt = log(n) / log(2) + 1;
    for(int k=1; k<=mt; k++) 
        for(int i=1; i<=n; i++) 
            f[i][k] = f[f[i][k-1]][k-1];

}

int lca(int a, int b) {
    if(depth[a] < depth[b]) swap(a,b);
    for(int k=mt; k>=0; k--) {
        if(depth[f[a][k]] >= depth[b]) {
            a = f[a][k];
        }
    }
    if(a == b) return a;
    for(int k=mt; k>=0; k--) {
        if(f[a][k] != f[b][k]) {
            a = f[a][k];
            b = f[b][k];
        }
    }
    return f[a][0];
}

void dfs_up(int x) {
    int now = depth[x] + w[x];
    int temp = barrel[now];
    vis[x] = 1;
    barrel[depth[x]] += st[x];
    for(int i=last[x]; i; i=e[i].to) {
        int v = e[i].v;
        if(vis[v]) continue;
        dfs_up(v);
    }
    ans[x] += barrel[now] - temp;
    for(int i=dif1[x].size()-1; i>=0; i--) {
        barrel[dif1[x][i]]--;//删除以x为lca的玩家,这样比x高的点就不会受到这个玩家的贡献
    }
}

void dfs_down(int x) {
    vis[x] = 1;
    int now = depth[x] - w[x] + 300000;
    int temp = barrel[now];
    for(int i=last[x]; i; i=e[i].to) {
        int v = e[i].v;
        if(vis[v]) continue;
        dfs_down(v);
    }
    for(int i=dif2[x].size()-1; i>=0; i--) {
        barrel[dif2[x][i]+300000]++;
    }
    ans[x] += barrel[now] - temp;
    for(int i=dif3[x].size()-1; i>=0; i--) {
        barrel[dif3[x][i]+300000]--;
    } 
}

int main() {
    r(n),r(m);
    for(int i=1; iint u,v;
        r(u),r(v);
        add(u,v);
        add(v,u);
    }

    depth[1] = 1; //¸ù½ÚµãÉî¶ÈΪ1 
    init_dfs(1);

    for(int i=1; i<=n; i++) 
        r(w[i]);

    init_lca(); 

    for(int i=1; i<=m; i++) {
        r(pla[i].s), r(pla[i].t);
        st[pla[i].s]++;
        pla[i].lca = lca(pla[i].s, pla[i].t);
        pla[i].len = depth[pla[i].s] + depth[pla[i].t] - 2 * depth[pla[i].lca];
        dif1[pla[i].lca].push_back(depth[pla[i].s]);
        dif2[pla[i].t].push_back(depth[pla[i].t] - pla[i].len);
        dif3[pla[i].lca].push_back(depth[pla[i].t] - pla[i].len);
    }

    dfs_up(1);

    memset(vis,0,sizeof(vis));
    memset(barrel,0,sizeof(barrel));

    dfs_down(1);

    for(int i=1; i<=m; i++) 
        if(depth[pla[i].s] - depth[pla[i].lca] == w[pla[i].lca]) 
            ans[pla[i].lca]--;
    for(int i=1; i<=n; i++) 
        printf("%d ",ans[i]);
    return 0;
}

你可能感兴趣的:(NOIP,图论,差分,LCA)