HDU4858 项目管理 其他

原文链接https://www.cnblogs.com/zhouzhendong/p/HDU4858.html

题目传送门 - HDU4858

题意

  给定一个无向图 $n$ 。有 $m$ 条边。

  每一个点有一个权值。

  有 $Q$ 次操作,有两种类型:

  1.  给一个点的权值加上 $v$ 。

  2.  对于每条关于 $x$ 的无向边,累加另一端点的权值,并输出最终的累加和。(可能会有重边)

  $n\leq 100000, m\leq n+10$ ,  $Q$ 数据范围不详。

题解

  这题本来是个分块题。

  但是,由于 $m\leq n+10$ ,我们就有了一种复杂度正确的简易算法。

  我们先 dfs 跑出原图的一个生成树。于是剩余的边就最多 10 条了。

  我们对于每一个点维护一下除父亲外的权值和,以及单点权值和。

  询问的时候就是第一种信息和父亲的单点权值和加起来。

  修改的时候,首先修改一下自己的单点权值和,然后修改一下父亲的单点权值和,然后暴力修改非树边所指向的点的“除父亲外的权值和”,即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=100025;
struct Gragh{
	static const int M=N*2;
	int cnt,y[M],nxt[M],fst[N];
	void clear(){
		cnt=1;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b){
		y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g,g2;
int T,n,m,Q,fa[N],flag[N],vis[N],v[N],sontot[N];
void dfs(int x,int pre){
	fa[x]=pre;
	vis[x]=1;
	for (int i=g.fst[x];i;i=g.nxt[i])
		if (!vis[g.y[i]]){
			flag[i>>1]=1;
			dfs(g.y[i],x);
		}
		else if (!flag[i>>1]){
			flag[i>>1]=1;
			g2.add(x,g.y[i]);
			g2.add(g.y[i],x);
		}
}
int main(){
	scanf("%d",&T);
	while (T--){
		scanf("%d%d",&n,&m);
		g.clear();
		for (int i=1,a,b;i<=m;i++){
			scanf("%d%d",&a,&b);
			g.add(a,b);
			g.add(b,a);
		}
		memset(fa,0,sizeof fa);
		memset(flag,0,sizeof flag);
		memset(vis,0,sizeof vis);
		memset(sontot,0,sizeof sontot);
		memset(v,0,sizeof v);
		g2.clear();
		dfs(1,0);
		scanf("%d",&Q);
		while (Q--){
			int opt,x,y;
			scanf("%d",&opt);
			if (opt==0){
				scanf("%d%d",&x,&y);
				if (fa[x])
					sontot[fa[x]]+=y;
				v[x]+=y;
				for (int i=g2.fst[x];i;i=g2.nxt[i])
					sontot[g2.y[i]]+=y;
			}
			else {
				scanf("%d",&x);
				printf("%d\n",v[fa[x]]+sontot[x]);
			}
		}
	}
	return 0;
}

  

posted @ 2018-07-21 22:23  zzd233  阅读(175)  评论(0编辑  收藏  举报