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; }