宝藏探寻

宝藏探寻 树上倍增

小μ发现了一个矿洞,里面出产珍贵的紫萤石。紫萤石的价值与它的质量成正比。可以认为,一个质量为x的紫萤石的价值为x2。经过探查,发现这个矿洞里面有一块巨大的紫萤石,它的结构是一个N个点,N-1条边的无向无环联通图。其中每一个节点i可以看成是一个质量为a[i]的紫萤石。有N-1条紫萤石边,每条边连接两个紫萤石节点,紫萤石边的质量可以忽略不计。显然这个巨大的紫萤石的价值为(Σa[i])2,然而直接带走这个巨大的紫萤石是不现实的,因此,需要选择一条路径(x,y),其中x和y是路径的两个端点,x≠y。现在要将x到y上的所有的紫萤石节点都炸碎。一个紫萤石节点被炸碎后,和它相连的边也会消失。现在有M次询问,每次给出路径的两个端点x,y(x≠y),你需要给出炸碎这条路径后,剩余的若干个紫萤石块的价值之和。每次询问是独立的。你可以认为,每次询问后整个紫萤石结构恢复原状。输入:第一行两个数:N<=200000,M<=100000第二行N个数:a[i]<=1000接下来M行,每行两个数x,y,表示一条以x和y为端点的路径。(x≠y)

​ 这道题考试的时候没想到倍增,然后只有五十分。。其实就是一个需要特殊处理的倍增而已。

#include <cstdio>
using namespace std;

typedef long long LL;
const LL maxn=2e5+5, logn=20;
inline void swap(LL &x, LL &y){
    LL tmp=x; x=y; y=tmp; }
inline LL sqr(LL x){ return x*x; }

class Graph{
public:
    struct Edge{
        LL to, next; Graph *belong;
        void set(LL x, LL y, Graph *g){
            to=x; next=y; belong=g; }
        inline LL operator*(){ return to; }
        Edge& operator++(){
            return *this=belong->edge[next]; }
    };
    void addedge(LL x, LL y){
        edge[++cntedge].set(y, fir[x], this);
        fir[x]=cntedge;
    }
    Edge& getlink(LL x){ return edge[fir[x]]; }
private:
    LL cntedge, fir[maxn];
    Edge edge[maxn*2];
};

Graph g;
LL n, m, a[maxn], cnta[maxn], dep[maxn], fa[maxn];
LL p[maxn][logn], f[maxn][logn];

void predfs(LL now, LL pre){
    Graph::Edge e;
    p[now][0]=now; fa[now]=pre;
    for (LL i=1; i<logn; ++i)
        p[now][i]=p[fa[p[now][i-1]]][i-1];
    cnta[now]+=a[now];
    for (e=g.getlink(now); *e; ++e){
        if (*e==pre) continue;
        dep[*e]=dep[now]+1;
        predfs(*e, now);
        cnta[now]+=cnta[*e];
    }
}

void dfs(LL now, LL pre){ //倍增
    Graph::Edge e;
    for (e=g.getlink(now); *e; ++e)
        if (*e!=pre) f[now][0]+=sqr(cnta[*e]);
    for (LL i=1; i<logn; ++i)
        f[now][i]=f[now][i-1]+f[fa[p[now][i-1]]][i-1]
                -sqr(cnta[p[now][i-1]]);
    for (e=g.getlink(now); *e; ++e)
         if (*e!=pre) dfs(*e, now);
}

LL solve(LL x, LL y){
    if (dep[x]>dep[y]) swap(x, y);
    LL ans=0, minusx=0, minusy=0;
    for (LL i=logn-1; i>=0; --i)
        if (dep[x]<dep[p[y][i]]){
            ans+=f[y][i]-minusy;
            minusy=sqr(cnta[p[y][i]]);
            y=fa[p[y][i]];
        }
    for (LL i=logn-1; i>=0; --i)
        if (p[x][i]!=p[y][i]){
            ans+=f[x][i]+f[y][i]-minusx-minusy;
            minusx=sqr(cnta[p[x][i]]);
            minusy=sqr(cnta[p[y][i]]);
            x=fa[p[x][i]]; y=fa[p[y][i]];
        }
    LL lca=x; ans+=f[lca][0]-minusx-minusy;
    ans+=sqr(cnta[1]-cnta[lca]);
    return ans;
}

int main(){
    scanf("%lld%lld", &n, &m);
    for (LL i=1; i<=n; ++i) scanf("%lld", &a[i]);
    LL x, y;
    for (LL i=1; i<n; ++i){
        scanf("%lld%lld", &x, &y);
        g.addedge(x, y); g.addedge(y, x);
    }
    predfs(1, 0);
    dfs(1, 0);
    for (LL i=0; i<m; ++i){
        scanf("%lld%lld", &x, &y);
        printf("%lld\n", solve(x, y));
    }
    return 0;
}
posted @ 2017-10-23 11:16  pechpo  阅读(257)  评论(0编辑  收藏  举报