1 条题解

  • 0
    @ 2025-8-24 22:34:21

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Noby_Glds
    be serious

    搬运于2025-08-24 22:34:21,当前版本为作者最后更新于2022-05-24 13:13:44,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    算法标签才是一切灵感的源泉

    思路:

    打开标签一看:缩点。

    我们分析下面给的图,可以发现,我们把两条从 1166 的路径叠起来,就可以覆盖由红色框框圈住的强连通分量,从而获得该强连通分量内所有的点的权值。

    因此我们可以先缩点,把图简化成一棵树。

    再打开标签一看:LCA。

    接下来,因为一条边只能经过一次,所以会出现下面这种情况:

    (注:此图跟上图不匹配。)

    发现没?不就是求两个点的 LCA,再把这两个点与 LCA 之间所有的点(包括这两个点和 LCA)的权值加入答案嘛。

    但如何维护答案呢?

    又打开标签一看:差分。

    我们可以用树上差分来记录一个点的经过次数该点的儿子的经过次数的差。

    最后往下一个 dfs,把差分数组合并,如果该点被经过至少一次,则把该点权值加入答案。

    代码:

    #include<bits/stdc++.h>
    #define N 2000010
    #define int long long
    using namespace std;
    struct hhh{
    	int v,next;
    }dl[2*N];
    int n,m,q,x,y,u[N],v[N],tot,cnt,top,sum,logn,ans;
    int a[N],head[N],dfn[N],low[N],stk[N],vis[N],col[N],jh[N],used[N],dep[N],f[N][30],yys[N];//yys即差分数组
    void qxx(int u,int v){
    	dl[++tot].v=v;
    	dl[tot].next=head[u];
    	head[u]=tot;
    }
    void tarjan(int x,int fa){//缩点
    	dfn[x]=low[x]=++cnt;
    	stk[++top]=x;
    	vis[x]=1;
    	for(int i=head[x];i;i=dl[i].next){
    		int v=dl[i].v;
    		if(v==fa) continue;
    		if(!dfn[v]) tarjan(v,x),low[x]=min(low[x],low[v]);
    		else if(vis[v]) low[x]=min(low[x],low[v]);
    	}
    	if(dfn[x]==low[x]){
    		sum++;
    		while(stk[top+1]!=x){
    			col[stk[top]]=sum;
    			jh[sum]+=a[stk[top]];
    			vis[stk[top]]=0;
    			top--;
    		}
    	}
    }
    void build(int u,int h){//建立LCA
    	dep[u]=h;
    	for(int i=1;i<=logn;i++){
    		if(h<=(1<<i)) break;
    		f[u][i]=f[f[u][i-1]][i-1];
    	}
    	for(int i=head[u];i;i=dl[i].next){
    		int v=dl[i].v;
    		if(vis[v]) continue;
    		vis[v]=1;
    		f[v][0]=u;
    		build(v,h+1);
    	}
    }
    int lca(int x,int y){//求LCA
    	if(dep[x]<dep[y]) swap(x,y);
    	int cha=dep[x]-dep[y];
    	for(int i=0;i<=logn;i++) if(cha&(1<<i)) x=f[x][i];
    	if(x==y) return x;
    	for(int i=logn;i>=0;i--){
    		if(!dep[f[x][i]]||f[x][i]==f[y][i]) continue;
    		x=f[x][i],y=f[y][i];
    	}
    	return f[x][0];
    }
    void Go(int x){//合并差分数组
    	for(int i=head[x];i;i=dl[i].next){
    		int v=dl[i].v;
    		if(v==f[x][0]) continue;
    		Go(v);
    		yys[x]+=yys[v]; 
    	}
    	if(yys[x]) ans+=jh[x];
    }
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0),cout.tie(0);
    	cin>>n>>m;
    	for(int i=1;i<=n;i++) cin>>a[i];
    	for(int i=1;i<=m;i++){
    		cin>>u[i]>>v[i];
    		qxx(u[i],v[i]),qxx(v[i],u[i]);
    	}
    	for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,-1);
    	tot=0;
    	memset(head,0,sizeof(head));
    	for(int i=1;i<=m;i++) if(col[u[i]]!=col[v[i]]) qxx(col[u[i]],col[v[i]]),qxx(col[v[i]],col[u[i]]);
    	vis[1]=1;
    	logn=log2(sum)+1;
    	build(1,1);
    	cin>>q;
    	while(q--){
    		cin>>x>>y;
    		int fa=lca(col[x],col[y]);
    		yys[col[x]]++,yys[col[y]]++;
    		yys[fa]--,yys[f[fa][0]]--;
    	}
    	Go(1);
    	cout<<ans;
    	return 0;
    }
    
    • 1

    信息

    ID
    7209
    时间
    1000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者