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

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

Day2

[ Submit][ Status][ Discuss]

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

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

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

#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#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 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]



你可能感兴趣的:(并查集,线段树,可并堆)