bzoj 2333: [SCOI2011]棘手的操作(线段树+离线操作,可并堆+set)

34 篇文章 0 订阅
7 篇文章 0 订阅

2333: [SCOI2011]棘手的操作

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1554   Solved: 588
[ Submit][ Status][ Discuss]

Description

N个节点,标号从1N,这N个节点一开始相互不连通。第i个节点的初始权值为a[i],接下来有如下一些操作:

U x y: 加一条边,连接第x个节点和第y个节点

A1 x v: 将第x个节点的权值增加v

A2 x v: 将第x个节点所在的连通块的所有节点的权值都增加v

A3 v: 将所有节点的权值都增加v

F1 x: 输出第x个节点当前的权值

F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值

F3: 输出所有节点中,权值最大的节点的权值

Input

 

输入的第一行是一个整数N,代表节点个数。

接下来一行输入N个整数,a[1], a[2], …, a[N],代表N个节点的初始权值。

再下一行输入一个整数Q,代表接下来的操作数。

最后输入Q行,每行的格式如题目描述所示。

Output

对于操作F1, F2, F3,输出对应的结果,每个结果占一行。

Sample Input

3

0 0 0

8

A1 3 -20

A1 2 20

U 1 3

A2 1 10

F1 3

F2 3

A3 -10

F3

Sample Output


-10

10

10

HINT



 对于30%的数据,保证 N<=100,Q<=10000


对于80%的数据,保证 N<=100000,Q<=100000


对于100%的数据,保证 N<=300000,Q<=300000


对于所有的数据,保证输入合法,并且 -1000<=v, a[1], a[2], …, a[N]<=1000

Source

[ Submit][ Status][ Discuss]

题解:线段树+离线操作。

这道题貌似是可以用可并堆或者堆套堆的,但是我太水了,并不会,只好用线段树来搞一搞啦。

先离线所有询问,对于所有的U操作先进性预处理,按照读入的顺序用并查集把一个连通块内的点并到一起,并不断的更新每个连通块的最后一个节点。然后按照每个连通块的顺序,把同一个连通块中的节点放到一起,然后用线段树维护。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 300003
#define inf 1000000000
using namespace std;
int n,m;
int tr[N*4],delta[N*4],maxn[N*4],a[N];
int op[N],x[N],v[N],sz,pos[N],val[N];
int fa[N],end[N],next[N];
void init()
{
	scanf("%d",&m);
	for (int i=1;i<=m;i++)
	{
		char s[10]; scanf("%s",s);
		if (s[0]=='A')
		 {
		 	if (s[1]=='1')
		 	 op[i]=2,scanf("%d%d",&x[i],&v[i]);// 将第x个节点的权值增加v
		 	if (s[1]=='2')
		 	 op[i]=3,scanf("%d%d",&x[i],&v[i]);//将第x个节点所在的连通块的所有节点的权值都增加v
		 	if (s[1]=='3')
		 	 op[i]=4,scanf("%d%d",&v[i]);//将所有节点的权值都增加v
		 }
		if (s[0]=='U')
		 op[i]=1,scanf("%d%d",&x[i],&v[i]);//加一条边,连接第x个节点和第y个节点
		if (s[0]=='F')
		 {
		 	if (s[1]=='1')
		 	 op[i]=5,scanf("%d",&x[i]);//输出第x个节点当前的权值
		 	if (s[1]=='2')
		 	 op[i]=6,scanf("%d",&x[i]);//输出第x个节点所在的连通块中,权值最大的节点的权值
		 	if (s[1]=='3')
		 	 op[i]=7;// 输出所有节点中,权值最大的节点的权值
		 }
	}
}
int find(int x)
{
	if (fa[x]==x)
	 return x;
	fa[x]=find(fa[x]);
	return fa[x];
}
void update(int x)
{
	tr[x]=max(tr[x<<1],tr[x<<1|1]);
}
void pushdown(int x)
{
	if (!delta[x]) return;
	delta[x<<1]+=delta[x];
	delta[x<<1|1]+=delta[x];
	tr[x<<1]+=delta[x];
	tr[x<<1|1]+=delta[x];
	delta[x]=0;
}
void build(int now,int l,int r)
{
	if (l==r)
	{
	 tr[now]=val[l];
	 return;
    }
    int mid=(l+r)/2;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}
void pointchange(int now,int l,int r,int x,int v)
{
	if (l==r)
	 {
	 	tr[now]+=v;
	 	return;
	 }
	pushdown(now);
	int mid=(l+r)/2;
	if (x<=mid)
	 pointchange(now<<1,l,mid,x,v);
	else
	 pointchange(now<<1|1,mid+1,r,x,v);
	update(now);
}
int findpoint(int now,int l,int r,int x)
{
	if (l==r) return tr[now];
	pushdown(now);
	int mid=(l+r)/2;
	if (x<=mid)
	 findpoint(now<<1,l,mid,x);
	else
	 findpoint(now<<1|1,mid+1,r,x);
}
void query(int now,int l,int r,int ll,int rr,int x)
{
	if (l>=ll&&r<=rr)
	 {
	 	tr[now]+=x;
	 	delta[now]+=x;
	 	return;
	 }
	pushdown(now);
	int mid=(l+r)/2;
    if (ll<=mid)
     query(now<<1,l,mid,ll,rr,x);
    if (rr>mid)
     query(now<<1|1,mid+1,r,ll,rr,x);
    update(now);
}
int qjmax(int now,int l,int r,int ll,int rr)
{
	if (l>=ll&&r<=rr)
	 return tr[now];
	pushdown(now);
	int mid=(l+r)/2;
	int ans=-inf;
	if (ll<=mid)
	 ans=max(ans,qjmax(now<<1,l,mid,ll,rr));
	if (rr>mid)
	 ans=max(ans,qjmax(now<<1|1,mid+1,r,ll,rr));
	return ans;
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	 scanf("%d",&a[i]);
	init();
	for (int i=1;i<=n;i++)
	 fa[i]=i,end[i]=i;
    for (int i=1;i<=m;i++)
     if (op[i]==1)
      {
      	int r1=find(x[i]),r2=find(v[i]);
      	if (r1==r2) continue;
      	fa[r2]=r1;//并查集的代表节点
      	next[end[r1]]=r2;//用next数组连通块中的点连接
      	end[r1]=end[r2];//记录连通块的结束节点
      }
    for (int i=1;i<=n;i++)
     if (find(i)==i)
     {
     	for (int j=i;j;j=next[j])
     	 	sz++,pos[j]=sz,val[sz]=a[j];
     }
    build(1,1,n);
    for (int i=1;i<=n;i++)
     fa[i]=i,end[i]=i;
    for (int i=1;i<=m;i++)
     {
     	if (op[i]==1)
     	 {
     	 	int r1=find(x[i]),r2=find(v[i]);
     	 	if (r1==r2) continue;
     	 	fa[r2]=r1;
     	 	end[r1]=end[r2];
     	 }
     	if (op[i]==2)
     	 pointchange(1,1,n,pos[x[i]],v[i]);
     	if (op[i]==3)
     	 query(1,1,n,pos[find(x[i])],pos[end[find(x[i])]],v[i]);
     	if (op[i]==4)
     	 query(1,1,n,1,n,v[i]);
     	if (op[i]==5)
     	 printf("%d\n",findpoint(1,1,n,pos[x[i]]));
     	if (op[i]==6)
     	 printf("%d\n",qjmax(1,1,n,pos[find(x[i])],pos[end[find(x[i])]]));
     	if (op[i]==7)
     	 printf("%d\n",qjmax(1,1,n,1,n));
     }
}


题解:可并堆+set

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#define N 300003
#define inf 1000000000
using namespace std;
int fa[N],lch[N],rch[N],size[N],val[N],delta[N],d[N];
int f[N];
int n,m,mx,sum;
multiset<int> p;
int find(int x)
{
	if (fa[x]==x) return x;
	fa[x]=find(fa[x]);
	return fa[x];
}
void pushdown(int now)
{
	int l=lch[now]; int r=rch[now];
	if (delta[now]) {
		if(l) val[l]+=delta[now],delta[l]+=delta[now];
		if(r) val[r]+=delta[now],delta[r]+=delta[now];
		delta[now]=0;
	} 
}
int merge(int x,int y)
{
	if (!x) return y;
	if (!y) return x;
	if (val[x]<val[y]) swap(x,y);
	pushdown(x);
	int t=merge(rch[x],y);
	f[t]=x; rch[x]=t;
	if (d[lch[x]]<d[rch[x]]) swap(lch[x],rch[x]);
	d[x]=d[rch[x]]+1;
	return x;
}
int get(int x)
{
	return lch[f[x]]==x;
}
void solve(int x,int r1)
{
	if (!lch[x]&&!rch[x]) {
		if (x==r1) {
			p.insert(val[x]);
			return;
		}
		int which=get(x);
		if (which) lch[f[x]]=0;
		else rch[f[x]]=0;
		f[x]=lch[x]=rch[x]=0;
		fa[r1]=fa[x]=merge(r1,x);
		if(x==fa[r1]) {
		 p.erase(p.find(val[r1]));
		 p.insert(val[x]);
	    }
		return;
	}
	int l=lch[x]; int r=rch[x];
	pushdown(x);
	int which=get(x);
	int t=merge(l,r);
	if (x==r1) {
		f[x]=lch[x]=rch[x]=0; f[t]=0;
		p.insert(val[t]);
		fa[r1]=fa[t]=merge(t,x);
		if(x==fa[r1]) {
		  p.erase(p.find(val[t]));
		  p.insert(val[x]);
	    }
		return;
	}
	if (which) lch[f[x]]=t,f[t]=f[x];
	else rch[f[x]]=t,f[t]=f[x];
	f[x]=lch[x]=rch[x]=0;
	fa[r1]=fa[x]=merge(r1,x);
	if (x==fa[x]) {
	 p.erase(p.find(val[r1]));
	 p.insert(val[x]);
    }
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d",&n); mx=-1000000000;
	p.clear();
	for (int i=1;i<=n;i++) {
	 scanf("%d",&val[i]);
	 p.insert(val[i]); 
    }
	for (int i=1;i<=n;i++) fa[i]=i;
	scanf("%d",&m);
	for (int i=1;i<=m;i++) {
		int opt,x,y,v; char s[10]; 
		scanf("%s",s); 
		if (s[0]=='U') {
			scanf("%d%d",&x,&y);
			int r1=find(x); int r2=find(y);
			if (r1!=r2) {
				int t=merge(r1,r2);
				if (t==r1) {
				  p.erase(p.find(val[r2]));  }
				else 
				 p.erase(p.find(val[r1]));
				fa[r2]=fa[r1]=t;
			}
		}
		if (s[0]=='A'&&s[1]=='1') {
			scanf("%d%d",&x,&v);
			int now=f[x]; int r1=find(x); int k=0;
			while (now!=0) k+=delta[now],now=f[now];
			if (x==r1)  p.erase(p.find(val[x]));
			val[x]+=k+v;
			solve(x,r1);
		}
		if (s[0]=='A'&&s[1]=='2') {
			scanf("%d%d",&x,&v);
			int r1=find(x);
			p.erase(p.find(val[r1]));
			val[r1]+=v; delta[r1]+=v;
			p.insert(val[r1]);
			mx=max(mx,val[r1]);	
		}
		if (s[0]=='A'&&s[1]=='3') {
			scanf("%d",&v); sum+=v;
		}
		if (s[0]=='F'&&s[1]=='1') {
			scanf("%d",&x);
		    int now=f[x]; int r1=find(x); int k=0;
			while (now!=0) k+=delta[now],now=f[now];
			printf("%d\n",val[x]+sum+k);
		}
		if (s[0]=='F'&&s[1]=='2') {
			scanf("%d",&x);
			int r1=find(x);
			printf("%d\n",val[r1]+sum);
		}
		if (s[0]=='F'&&s[1]=='3') 
		  printf("%d\n",*--p.find(inf)+sum);
		//cout<<*--p.find(inf)<<endl;
	}
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值