HDU 3966 Aragorn's Story

把树链剖分搞了一下,树链剖分就是把某树按重链和轻链映射到某一线性的数组上去,这样对于某条路径的查询和修改就变成了树状数组和线段树的事了。

这道题在HDU上要加个爆栈的,而且还要用c++,g++也会栈溢出

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<math.h>
using namespace std;
#define out(x) cout<<#x<<": "<<x<<endl
const double eps(1e-8);
const int maxn=50100;
const long long inf=-1u>>1;
typedef long long ll;
struct Edge{
	int v,next;
}edge[maxn*2];
int n,m,tot,pos;

int son[maxn],fa[maxn],head[maxn],num[maxn],deep[maxn],p[maxn],top[maxn];
int c[maxn],a[maxn];
char op[10];
void init() {
	memset(head,-1,sizeof(head));
	memset(son,-1,sizeof(son));
	memset(c,0,sizeof(c));
	tot=0;pos=1;
}
void addedge(int u,int v){
	edge[tot].v=v;
	edge[tot].next=head[u];
	head[u]=tot++;
}
void dfs1(int u,int pre,int d){//第一次dfs,计算结点数,深度,重儿子,父结点
	deep[u]=d;
	num[u]=1;
	fa[u]=pre;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v==pre) continue;
		dfs1(v,u,d+1);
		num[u]+=num[v];
		if(son[u]==-1||num[v]>num[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int sp){//第二次dfs,计算链的根,每一结点对应树状数组的值 
	top[u]=sp;
	p[u]=pos++;
	//fp[p[u]]=u;
	if(son[u]==-1) return;
	dfs2(son[u],sp);
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v!=fa[u]&&v!=son[u]) dfs2(v,v);//不是重儿子 
	}
}


void add(int i,int val){
	while(i<=n){
		c[i]+=val;
		i+=i&(-i);
	}
}
int sum(int i){
	int s=0;
	while(i>0){
		s+=c[i];
		i-=(-i)&i;
	}
	return s;
}
void change(int u,int v,int val) {
	
	int f1=top[u],f2=top[v];
	int tmp=0;
	
	while(f1!=f2){
		if(deep[f1]<deep[f2]){
			swap(f1,f2);
			swap(u,v);
		}
		add(p[f1],val);//把sum[i]表示为i点的值之后,对加上某段区间加上某值,对操作替换一下,就变成了区间首加上该值,并区间末+1减去该值 
		add(p[u]+1,-val);
		
		u=fa[f1];
		f1=top[u];
	}
	//printf("%d %d\n",p[u],p[v]+1);
	if(deep[u]>deep[v]) swap(u,v);
	add(p[u],val);
	add(p[v]+1,-val);
	//printf("dfsddf\n");
}
int main() {
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	int P;
	while(scanf("%d%d%d",&n,&m,&P)==3){
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		init();
		int u,v;
		int c1,c2,k,i;
		for(i=1;i<=m;i++){
			scanf("%d%d",&u,&v);
			addedge(u,v);
			addedge(v,u);
		}
		dfs1(1,0,1);
		dfs2(1,1);
		for(i=1;i<=n;i++){
			add(p[i],a[i]);
			printf("%d ",sum(p[i]));
			add(p[i]+1,-a[i]);//在这里我们每次加上a[i]之后,还要减去a[i],以保证sum[i]==第i项的值 
			printf("%d\n",sum(p[i]+1));
		}
		
		while(P--) {
			scanf("%s",op);
			if(op[0]=='Q'){
				scanf("%d",&u);
				printf("%d\n",sum(p[u]));
			}
			else {
				scanf("%d%d%d",&c1,&c2,&k);
				if(op[0]=='D') k=-k;
				change(c1,c2,k);
			}
		}
	}
	return 0;
}
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值