Query on a graph HDU5957 沈阳ICPC重现赛

Query on a graph

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 369    Accepted Submission(s): 81


Problem Description
You are given a connected simple graph(in which both multiple edges and loops are disallowed) with N nodes and N edges. In this graph each node has a weight, and each edge has the same length of one unit. Define D(u,v) as the distance between node u and node v. Define S(u,k) as the set of nodes x which satisfy D(u,x) ≤ k.
We will ask you to perform some instructions of the following forms.
MODIFY u k d: weight of all nodes in S(u,k) increase by d or decrease by -d. 
QUERY u k: ask for the sum of weight of all nodes in S(u,k).
In the beginning, the weight of all nodes are 0.
 

Input
The first line of input contains an integer t, the number of test cases. t test cases follow. For each test case, in the first line there is an integer N(N ≤ 100000). The i-th line of the next N line describes the i-th edge: two integers u,v denotes an edge between u and v. In the next line, an integer Q(Q ≤ 100000) indicates the number of instructions. Next Q lines contain instructions MODIFY u k d or QUERY u k, where |d|≤ 100 and 0 ≤ k ≤ 2.
 

Output
For each QUERY instruction, output a integer in a line.
 

Sample Input
  
  
2 6 1 2 2 3 3 4 4 1 4 5 3 6 5 MODIFY 1 1 3 MODIFY 3 1 2 MODIFY 5 2 1 QUERY 3 2 QUERY 4 1 6 1 2 2 3 3 1 1 4 2 5 3 6 5 MODIFY 3 1 5 MODIFY 2 2 2 QUERY 6 1 MODIFY 4 1 -2 QUERY 2 2
 

Sample Output
  
  
21 14 14 28
 

Source
------------------------------------------解题吐槽---------------------------------

沈阳icpc重现赛,一开始就去开J题,一眼这不是tarjan算法+线段树区间修改维护bfs序嘛,我能写!赶紧开始想J题,发现有点麻烦啊,估计要写200行.....写着写着发现可能要写300行,过一会儿又发现400行好像差不多.....4个小时过去了发现已经码了500多行了......[捂脸][捂脸][捂脸]。写完交,一WA再WA......后来发现是边界写跪了........再改终于AC......翻了翻去年的榜,只有8个队做.......一开始如果我看到榜,大概我是绝对不会去写的.......

----------------------------------------------------------------------------------------

题解:看到修改和查询的题目就往数据结构上去想。

而到某个点的距离 <= k的一些数全部+或-某个值,我们想到按照bfs序将树展开,展开之后这些数就对应到了链上的某一段连续的序列了,就可以利用线段树的区间修改来维护这段bfs序了。

这里有个麻烦的地方是,给出来的图是一个基环外向树,因此,我们先用tarjan算法讲环找出来(当然也可以用其他更为简单的算法找环),然后把环去掉,形成森林,对森林中的每一棵树都做一遍bfs序,然后再跑一边dfs序,得到从每个点u出发,走一步的儿子对应区间左右端点。走两步的孙子对应的区间左右端点。这样的话,就可以将这棵树映射到一颗线段树上了(注意线段树要动态开点,不然会MLE)。

下面就要开始实现修改和查询的功能了。

这两种功能实现起来类似(代码复制粘贴以后稍微改一改就好了)。

这里就直说区间修改。

为了方便起见,我们还需要对基环上的点进行一次dfs操作,以便将基环展开为一条链,存到数组上。

这有很多种情况:

1.k = 0的时候,如果u在基环上且不在树上直接在链上对应的位置+addval

如果u在某颗树上的话,要在这棵树对应的线段树里+addval

2.k = 1的时候,如果u在基环上的话,pre 找到基环上左边的点,该点执行k=0时候的操作,post找到基环上右边的点,该点也执行k = 0的操作

然后u本身也要执行k = 0的操作,然后如果u还在树上的话,对u儿子对应的区间进行+addval操作。

3.k = 2的时候,很麻烦。。。。仔细考虑一下吧(例如要对u的孙子节点+addval,对u的儿子节点+addval,对u的父亲节点的儿子节点+addval等等等等,情况很多,具体请参照我的代码看)


500多行的代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e5+10;
int cas = 0; 
struct segtree{
	LL *val;
	LL *addmark;
	int n;
	void init(int N){
		n = N;
		val = new LL[(n<<2)+10];
		addmark = new LL[(n<<2)+10]; 
		memset(val,0,sizeof(LL) * ((n<<2) + 10));
		memset(addmark,0,sizeof(LL) * ((n<<2) + 10));
	}
	void pushdown(int root,int L,int R){
		if(addmark[root]){
			int mid = (L + R) / 2;
			addmark[root*2] += addmark[root] ;
			addmark[root*2+1] += addmark[root] ;
			val[root*2] += addmark[root] * (mid - L + 1);
			val[root*2+1] += addmark[root] * (R - mid);
			addmark[root] = 0;
		}
	}
	void pushup(int root){
		val[root] = val[root * 2] + val[root * 2 + 1];
	}
	
	LL query(int root,int begin,int end,int left,int right)
	{
	    int p1,p2;
	    if(left > end||right < begin)
	        return 0;
	    if(begin>=left&&end<=right)
	        return val[root];
	    pushdown(root,begin,end);
		LL a = query(root*2,begin,(begin+end)/2,left,right);
		LL b = query(root*2+1,(begin+end)/2+1,end,left,right);
		return a + b;
	}
	void update(int root,int begin,int end,int ubegin,int uend,LL addval)
	{
	    if(ubegin > end || uend < begin)
	        return ;
		if(begin >= ubegin && end <= uend){
			addmark[root] += addval;
			val[root] += addval * (end - begin + 1);
			return ;
		}
		pushdown(root,begin,end);
		int m = (begin+end)/2;
		update(root*2,begin,m,ubegin,uend,addval);
		update(root*2+1,m+1,end,ubegin,uend,addval);
	    pushup(root);
	}
}segs[MAXN];


int head[MAXN];
int cnt;
struct edge{ 
    int v; 
    int next; 
    int cost; 
}Es[MAXN<<1];  
void init(){ 
    cnt = 0; 
    memset(head,-1,sizeof(head)); 
}
inline void add_edge(int i,int j,int cost){   
    Es[cnt].v = j; 
    Es[cnt].cost = cost; 
    Es[cnt].next = head[i]; 
    head[i] = cnt++; 
}   
int n;
int DFN[MAXN],LOW[MAXN];
int stk[MAXN],vis[MAXN],belong[MAXN];
int idx,sccnum,tot;
vector<int> scc[MAXN];
void tarjan(int x,int fa){
	DFN[x] = LOW[x] = ++ tot;
	stk[++idx] = x;
	vis[x] = 1;
	for(int e = head[x];e != -1;e = Es[e].next){
		int v = Es[e].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],DFN[v]);
		}
	}
	if(DFN[x] == LOW[x]){
		++sccnum;
		int item;
		do{
			item = stk[idx--];
			belong[item] = sccnum;
			scc[sccnum].push_back(item);
			vis[item] = 0;
		}while(x != item);
	}
}
int pa[MAXN],dep[MAXN],bfs_order[MAXN][3][2],pos[MAXN],siz[MAXN],loop_link[MAXN];
int belong_tree[MAXN];
LL loop_val[MAXN];
int loop_pos[MAXN];
int loop,link_cnt;
vector<int> orders[MAXN];
void bfs(int x){
	map<int,int> vismp;
	vismp.clear();
	queue<int> Q;
	vismp[x] = 1;
	dep[x] = 0;
	Q.push(x);
	pos[x] = 0;
	orders[x].push_back(x);
	while(!Q.empty()){
		int u = Q.front();Q.pop();
		for(int e = head[u];e != -1;e = Es[e].next){
			int v = Es[e].v;
			if(vismp[v] || belong[v] == loop) continue;
			vismp[v] = 1;
			dep[v] = dep[u] + 1;
			pa[v] = u;
			pos[v] = orders[x].size();
			orders[x].push_back(v);
			Q.push(v);
		}
	}
}
void deal(int rt){
	if(!belong_tree[rt]) belong_tree[rt] = rt;
	siz[rt] = 1;
	bfs_order[rt][0][0] = bfs_order[rt][0][1] = pos[rt];
	int x1 = -1,y1 = -1;
	int x2 = -1,y2 = -1;
	for(int e = head[rt];e != -1;e = Es[e].next){
		int v = Es[e].v;
		if(v == pa[rt] || belong[v] == loop) continue;
		belong_tree[v] = belong_tree[rt];
		deal(v);
		siz[rt] += siz[v];
		if(x1 == -1 && y1 == -1){
			x1 = y1 = pos[v];
		}
		else if(bfs_order[v][0][0] != -1 && bfs_order[v][0][1] != -1){
			x1 = min(x1,bfs_order[v][0][0]);
			y1 = max(y1,bfs_order[v][0][1]);
		}
		if(x2 == -1 && y2 == -1){
			x2 = bfs_order[v][1][0];
			y2 = bfs_order[v][1][1];
		}
		else if (bfs_order[v][1][0] != -1 && bfs_order[v][1][1] != -1){
			x2 = min(x2,bfs_order[v][1][0]);
			y2 = max(y2,bfs_order[v][1][1]);
		}
	}
	bfs_order[rt][1][0] = x1,bfs_order[rt][1][1] = y1;
	bfs_order[rt][2][0] = x2,bfs_order[rt][2][1] = y2;
}
LL ask_zero(int u){
	int p = belong_tree[u];
	if(p){
		int l = bfs_order[u][0][0]+1;
		int r = bfs_order[u][0][1]+1;
		return segs[p].query(1,1,segs[p].n,l,r);
	}
	else{
		return loop_val[loop_pos[u]];
	}
}
LL ask_tree_one(int u){
	int p = belong_tree[u];
	if(p){
		int l = bfs_order[u][1][0]+1;
		int r = bfs_order[u][1][1]+1;
		if(l && r)
			return segs[p].query(1,1,segs[p].n,l,r);
	}
	return 0;
}
LL ask_tree_two(int u){
	int p = belong_tree[u];
	if(p){
		int l = bfs_order[u][2][0]+1;
		int r = bfs_order[u][2][1]+1;
		if(l  && r )
			return segs[p].query(1,1,segs[p].n,l,r);
	} 
	return 0;
}

void md_zero(int u,int d){
	int p = belong_tree[u];
	if(p){
		int l = bfs_order[u][0][0]+1;
		int r = bfs_order[u][0][1]+1;
		segs[p].update(1,1,segs[p].n,l,r,d);
	}
	else{
		loop_val[loop_pos[u]] += d;
	}
}
void md_tree_one(int u,int d){
	int p = belong_tree[u];
	if(!p) return ;
	int l = bfs_order[u][1][0]+1;
	int r = bfs_order[u][1][1]+1;
	if(l && r)
		segs[p].update(1,1,segs[p].n,l,r,d);
}
void md_tree_two(int u,int d){
	int p = belong_tree[u];
	if(!p) return ;
	int l = bfs_order[u][2][0]+1;
	int r = bfs_order[u][2][1]+1;
	if(l && r)
		segs[p].update(1,1,segs[p].n,l,r,d);
}
LL ask(int u,int k){
	if(k == 0){
		return ask_zero(u);
	}
	else if(k == 1){
		if(pa[u] == 0) // in the loop
		{
			LL ans = 0;
			int pre = (loop_pos[u] + link_cnt - 1) % link_cnt; 
			pre = loop_link[pre];
			int post = (loop_pos[u] + link_cnt + 1) % link_cnt;
			post = loop_link[post];
			ans += ask_zero(u);
			ans += ask_tree_one(u);
			ans += ask_zero(pre);
			ans += ask_zero(post);
			return ans;
		}
		else{
			LL ans = 0;
			ans += ask_zero(pa[u]);
			ans += ask_zero(u);
			ans += ask_tree_one(u);
			return ans;
		}
	}
	else if(k == 2){
		if(pa[u] == 0){
			LL ans = 0;
			map<int,int> vismp[3];
			vismp[0].clear(),vismp[1].clear(),vismp[2].clear();
			int pre_pre = (loop_pos[u] + link_cnt - 2) % link_cnt; 
			pre_pre = loop_link[pre_pre];
			int pre = (loop_pos[u] + link_cnt - 1) % link_cnt; 
			pre = loop_link[pre];
			int post = (loop_pos[u] + link_cnt + 1) % link_cnt;
			post = loop_link[post];
			int post_post = (loop_pos[u] + link_cnt + 2) % link_cnt;
			post_post = loop_link[post_post];
			if(!vismp[0][pre_pre]){
				vismp[0][pre_pre] = 1;
				ans += ask_zero(pre_pre);
			}
			if(!vismp[0][pre]){
				vismp[0][pre] = 1;
				ans += ask_zero(pre);
			}
			if(!vismp[1][pre]){
				vismp[1][pre] = 1;
				ans += ask_tree_one(pre);
			}
			if(!vismp[0][u]){
				vismp[0][u] = 1;
				ans += ask_zero(u);
			}
			if(!vismp[1][u]){
				vismp[1][u] = 1;
				ans += ask_tree_one(u);
			}
			if(!vismp[2][u]){
				vismp[2][u] = 1;
				ans += ask_tree_two(u);
			}
			if(!vismp[0][post_post]){
				vismp[0][post_post] = 1;
				ans += ask_zero(post_post);
			}
			if(!vismp[0][post]){
				vismp[0][post] = 1;
				ans += ask_zero(post);
			}
			if(!vismp[1][post]){
				vismp[1][post] = 1;
				ans += ask_tree_one(post);
			}
			return ans;
		}
		else if(pa[pa[u]] == 0){
			LL ans = 0;
			int fa = pa[u];
			int pre = (loop_pos[fa] + link_cnt - 1) % link_cnt; 
			pre = loop_link[pre];
			int post = (loop_pos[fa] + link_cnt + 1) % link_cnt;
			post = loop_link[post];
			ans += ask_zero(pre);
			ans += ask_zero(post);
			ans += ask_zero(fa);
			ans += ask_tree_one(fa);
			ans += ask_tree_one(u);
			ans += ask_tree_two(u);
			return ans;
		}
		else{
			LL ans = 0;
			ans += ask_zero(pa[pa[u]]);
			ans += ask_zero(pa[u]);
			ans += ask_tree_one(pa[u]);
			ans += ask_tree_one(u);
			ans += ask_tree_two(u);
			return ans;
		}
	}
}
void modify(int u,int k,int d){
	if(k == 0){
		md_zero(u,d);
	}
	else if(k == 1){
		if(pa[u] == 0) // in the loop
		{
			map<int,int> vismp;
			vismp.clear();
			int pre = (loop_pos[u] + link_cnt - 1) % link_cnt; 
			pre = loop_link[pre];
			int post = (loop_pos[u] + link_cnt + 1) % link_cnt;
			post = loop_link[post];
			vismp[u] = 1;
			md_zero(u,d);
			md_tree_one(u,d);
			if(!vismp[pre]){
				vismp[pre] = 1;
				md_zero(pre,d);
			}
			if(!vismp[post]){
				vismp[post] = 1;
				md_zero(post,d);
			}
		}
		else{
			md_zero(pa[u],d);
			md_zero(u,d);
			md_tree_one(u,d);
		}
	}
	else if(k == 2){
		if(pa[u] == 0)// in the loop 
		{
			map<int,int> vismp[3];
			vismp[0].clear(),vismp[1].clear(),vismp[2].clear();
			int pre_pre = (loop_pos[u] + link_cnt - 2) % link_cnt; 
			pre_pre = loop_link[pre_pre];
			int pre = (loop_pos[u] + link_cnt - 1) % link_cnt; 
			pre = loop_link[pre];
			int post = (loop_pos[u] + link_cnt + 1) % link_cnt;
			post = loop_link[post];
			int post_post = (loop_pos[u] + link_cnt + 2) % link_cnt;
			post_post = loop_link[post_post];
			if(!vismp[0][pre_pre]){
				vismp[0][pre_pre] = 1;
				md_zero(pre_pre,d);
			}
			if(!vismp[0][pre]){
				vismp[0][pre] = 1;
				md_zero(pre,d);
			}
			if(!vismp[1][pre]){
				vismp[1][pre] = 1;
				md_tree_one(pre,d);
			}
			if(!vismp[0][u]){
				vismp[0][u] = 1;
				md_zero(u,d);
			}
			if(!vismp[1][u]){
				vismp[1][u] = 1;
				md_tree_one(u,d);
			}
			if(!vismp[2][u]){
				vismp[2][u] = 1;
				md_tree_two(u,d);
			}
			if(!vismp[0][post_post]){
				vismp[0][post_post] = 1;
				md_zero(post_post,d);
			}
			if(!vismp[0][post]){
				vismp[0][post] = 1;
				md_zero(post,d);
			}
			if(!vismp[1][post]){
				vismp[1][post] = 1;
				md_tree_one(post,d);
			}
		}
		else if(pa[pa[u]] == 0){
			int fa = pa[u];
			int pre = (loop_pos[fa] + link_cnt - 1) % link_cnt; 
			pre = loop_link[pre];
			int post = (loop_pos[fa] + link_cnt + 1) % link_cnt;
			post = loop_link[post];
			md_zero(pre,d);
			md_zero(post,d);
			md_zero(fa,d);
			md_tree_one(fa,d);
			md_tree_one(u,d);
			md_tree_two(u,d);
		}
		else{
			md_zero(pa[pa[u]],d);
			md_zero(pa[u],d);
			md_tree_one(pa[u],d);
			md_tree_one(u,d);
			md_tree_two(u,d);
		}
	}
}

void dfs_loop(int u,map<int,int>& mpvis){
	for(int e = head[u];e != -1;e = Es[e].next){
		int v = Es[e].v;
		if(mpvis[v] || belong[v] != loop) continue;
		mpvis[v] = 1;
		loop_pos[v] = link_cnt;
		loop_link[link_cnt++] = v;
		dfs_loop(v,mpvis);
	}
}
void get_loops(){
	map<int,int> mpvis;
	int rt = scc[loop][0];
	mpvis[rt] = 1;
	loop_pos[rt] = link_cnt;
	loop_link[link_cnt++] = rt;
	dfs_loop(rt,mpvis);
}
void solve(){
	init();
	loop = idx = sccnum = tot = 0;
	memset(vis,0,sizeof(vis));
	memset(stk,0,sizeof(stk));
	memset(DFN,0,sizeof(DFN));
	memset(LOW,0,sizeof(LOW));
	memset(belong,0,sizeof(belong));
	memset(pa,0,sizeof(pa));
	memset(dep,0,sizeof(dep));
	memset(bfs_order,0,sizeof(bfs_order));
	memset(pos,0,sizeof(pos));
	memset(siz,0,sizeof(siz));
	memset(loop_link,0,sizeof(loop_link));
	memset(belong_tree,0,sizeof(belong_tree));
	memset(loop_val,0,sizeof(loop_val));
	link_cnt = 0;
	for(int i = 0;i < MAXN;i++){
		scc[i].clear();
		orders[i].clear();
	}
	scanf("%d",&n);
	for(int i = 1;i <= n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		add_edge(a,b,1);
		add_edge(b,a,1);
	}
	tarjan(1,0);

	for(int i = 1;i <= sccnum;++i){
		if(scc[i].size() > 1){
			loop = i;
		}
	}
	//vector<int> roots;
	//roots.clear();
	for(int i = 0;i < scc[loop].size();++i){
		int rt = scc[loop][i];
		bfs(rt);
		deal(rt);
		if(siz[rt] == 1) belong_tree[rt] = 0;
		segs[rt].init(siz[rt]+3);
	}
	get_loops();
	int m;
	scanf("%d",&m);
	char str[10];
	while(m--){ 
		if(str[0] == 'M'){
			int u,k,d;
			scanf("%d%d%d",&u,&k,&d);
			modify(u,k,d);
		}
		else{
			int u,k;
			scanf("%d%d",&u,&k);
			LL ans;
			ans = ask(u,k);
			printf("%lld\n",ans);
		}
	}
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		++cas;
		solve();
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值