【SCOI2011】【线段树】棘手的操作

15 篇文章 0 订阅

比较好的一道数据结构题。

初看题目很容易想到用并查集来维护森林中各个节点之间的关系,但是在合并的时候就会出现很多问题了。如果用并查集的话需要暴力地修改标记,维护最大值,然后需要堆来维护各个连通块的最优值,这样显得非常繁琐。

于是我们可以考虑设计离线算法,先将所有询问读入,把连通块之间的关系用并查集维护,并且保证大的节点接在小的节点后面,维护完之后我们就可以将每个连通块映射成为一个区间,这样题目中不管是询问还是更新都是在区间上操作。而在区间上操作的数据结构我们马上就可以想到使用线段树,这样我们可以以O(m)的预处理和O(mlogn)的时间来解决本题。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define lson l,m,st << 1
#define rson m + 1,r,st << 1 | 1
using namespace std;
const int maxn = 300000 + 10;
const int inf = 0x3f3f3f3f;
struct Ques
{
	char op[4];
	int x,v;
}q[maxn];
int fa[maxn],end[maxn],pos[maxn],link[maxn],weight[maxn];
int Max[maxn<<2],add[maxn<<2];
int n,m;

void init()
{
	freopen("bzoj2333.in","r",stdin);
	freopen("bzoj2333.out","w",stdout);
}

void readdata()
{
	scanf("%d",&n);
	for(int i = 1;i <= n;i++)scanf("%d",&weight[i]);
	scanf("%d",&m);
	for(int i = 1;i <= m;i++)
	{
		scanf("%s",q[i].op);
		if(q[i].op[0] == 'F')
		{
			if(q[i].op[1] == '3')continue;
			else scanf("%d",&q[i].x);
		}
		else
		{
			if(q[i].op[1] == '3')scanf("%d",&q[i].x);
			else scanf("%d%d",&q[i].x,&q[i].v);
		}
	}

}

int find(int x)
{
	if(fa[x] == x)return x;
	fa[x] = find(fa[x]);
	return fa[x];
}

void make_sequence()
{
	for(int i = 1;i <= n;i++)end[i] = fa[i] = link[i] = i;
	for(int i = 1;i <= m;i++)
	{
		if(q[i].op[0] == 'U')
		{
			int x = find(q[i].x);
			int y = find(q[i].v);
			if(x > y)swap(x,y);
			fa[y] = x;
			link[end[x]] = y;
			end[x] = end[y];
		}
	}
	memset(end,0,sizeof(end));
	int cnt = 0;
	for(int i = 1;i <= n;i++)
	{
		int x = i;
		while(!end[x])
		{
			end[x] = ++cnt;
			pos[x] = end[x];
			fa[x] = x;
			x = link[x];
		}
	}
	memcpy(link,weight,sizeof(weight));
	for(int i = 1;i <= n;i++)
	{
		weight[pos[i]] = link[i];
	}
}

void pushup(int st)
{
	Max[st] = max(Max[st<<1] + add[st<<1],Max[st<<1|1] + add[st<<1|1]);
}

void build(int l,int r,int st)
{
	add[st] = 0;
	if(l == r)
	{
		Max[st] = weight[l];
		return;
	}
	int m = (l + r) >> 1;
	build(lson);
	build(rson);
	pushup(st);
}

void update(int L,int R,int c,int l,int r,int st)
{
	if(L <= l && r <= R)
	{
		add[st] += c;
		return;
	}
	int m = (l + r) >> 1;
	if(L <= m)update(L,R,c,lson);
	if(R > m)update(L,R,c,rson);
	pushup(st);
}

int query(int L,int R,int l,int r,int st)
{
	if(L <= l && r <= R)return Max[st] + add[st];
	int ret = -inf;
	int m = (l + r) >> 1;
	if(L <= m)ret = max(ret,query(L,R,lson));
	if(R > m)ret = max(ret,query(L,R,rson));
	return ret + add[st];
}

void solve()
{
	make_sequence();
	build(1,n,1);
	for(int i = 1;i <= m;i++)
	{
		if(!strcmp(q[i].op,"U"))
		{
			int x = find(q[i].x);
			int y = find(q[i].v);
			if(x > y)swap(x,y);
			fa[y] = fa[x];
			end[x] = end[y];
		}
		if(!strcmp(q[i].op,"A1"))update(pos[q[i].x],pos[q[i].x],q[i].v,1,n,1);
		if(!strcmp(q[i].op,"A2"))
		{
			int x = find(q[i].x);
			update(pos[x],end[x],q[i].v,1,n,1);
		}
		if(!strcmp(q[i].op,"A3"))update(1,n,q[i].x,1,n,1);
		if(!strcmp(q[i].op,"F1"))printf("%d\n",query(pos[q[i].x],pos[q[i].x],1,n,1));
		if(!strcmp(q[i].op,"F2"))
		{
			int x = find(q[i].x);
			printf("%d\n",query(pos[x],end[x],1,n,1));
		}
		if(!strcmp(q[i].op,"F3"))printf("%d\n",query(1,n,1,n,1));
	}
}

int main()
{
	init();
	readdata();
	solve();
	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值