宝藏探寻 树上倍增
小μ发现了一个矿洞,里面出产珍贵的紫萤石。紫萤石的价值与它的质量成正比。可以认为,一个质量为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
", solve(x, y));
}
return 0;
}