缩点+树上差分的板子,提交记录和代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,iindex;
int p[500015];//点权
int a[500015];//缩点后的点权
int scc[500015];//缩点后存点的数组
int dfn[500015],low[500015];//dfn时间戳(访问到当前节点的时间),low为当前环(如果有的话)的起点节点
int stac[500015],top;//栈
vector<int>e[500015];//vector建边
int cnt=0;//统计缩点数量
void tarjan(int x,int fa){
if(dfn[x])return;//已被访问过
low[x]=dfn[x]=++iindex;//时间戳和low数组处理
stac[++top]=x;//入栈
for(int i=0;i<e[x].size();i++){
int v=e[x][i];
if(v==fa)continue;
if(!dfn[v]){//当前点未被访问过
tarjan(v,x);//tarjan下一层(可能有环,也可能在链上)
low[x]=min(low[x],low[v]);//如果此处有环,low[x]将被修改
}
else if(!scc[v])low[x]=min(low[x],dfn[v]);//注意此处v不在环内才能修改low[x],保证算法正确性
}
if(dfn[x]==low[x]){//回到原点(将从x开始缩点)
int y;cnt++;
while(y=stac[top--]){
scc[y]=cnt;//缩点
a[cnt]+=p[y];//点权也缩
if(x==y)break;//缩完了跑路
}
}
}
vector<int>g[500015];//缩点后的图
int dep[500015];//树的深度
int jump[500015][19];
int b[500015];//差分数组
inline void swap(int &x,int &y){x^=y^=x^=y;return;}//据说是顶级优化swap
void dfs(int u,int f){
dep[u]=dep[f]+1;
jump[u][0]=f;
for(int j=1;j<=18;j++){
if(dep[u]<(j<<1))break;
jump[u][j]=jump[jump[u][j-1]][j-1];
}
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(v==f)continue;
dfs(v,u);
}
}
inline int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int j=18;j>=0;j--){
if((dep[x]-dep[y]&(1<<j)))x=jump[x][j];
}
if(x==y)return x;
for(int j=18;j>=0;j--){
if(jump[x][j]!=jump[y][j]){
x=jump[x][j];
y=jump[y][j];
}
}
return jump[x][0];
}
void ask(int u,int f){
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if (v==f) continue;
ask(v,u);
b[u]+=b[v];
}
}
void debug(){//debug更方便理解缩点过程
cout<<"scc:";
for(int i=1;i<=n;i++)cout<<scc[i]<<' ';
cout<<"\ndfn:";
for(int i=1;i<=n;i++)cout<<dfn[i]<<' ';
cout<<"\nlow:";
for(int i=1;i<=n;i++)cout<<low[i]<<' ';
cout<<'\n';
return ;
}
void debug2(){
cout<<"Scc:";
for(int i=1;i<=n;i++){
cout<<scc[i]<<' ';
}cout<<'\n';
cout<<"b:";
for(int i=1;i<=n;i++){
cout<<b[scc[i]]<<' ';
}cout<<'\n';
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>p[i];
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
tarjan(1,0);//tarjan
//for(int i=1;i<=cnt;i++)cout<<a[i]<<' ';cout<<'\n';
//debug();
for(int i=1;i<=n;i++){
for(int j=0;j<e[i].size();j++){//建缩点后的图
int v=e[i][j];
if(scc[i]!=scc[v]){
g[scc[i]].push_back(scc[v]);
g[scc[v]].push_back(scc[i]);
}
}
}
dfs(scc[1],0);
int Q;
cin>>Q;
while(Q--){
int x,y;
cin>>x>>y;
x=scc[x];y=scc[y];
int u=lca(x,y);
b[x]++;b[y]++;b[u]--;b[jump[u][0]]--;
}
ask(scc[1],0);
int ans=0;
for(int i=1;i<=cnt;i++){
if(b[i])ans+=a[i];
}
//debug2();
cout<<ans;
}