zoukankan      html  css  js  c++  java
  • P3273[SCOI2011]棘手的操作【线段树,并查集】

    正题

    题目链接:https://www.luogu.com.cn/problem/P3273


    题目大意

    \(n\)个点有权值,要求支持操作

    1. 连接两个点
    2. 单点加权
    3. 联通块加权
    4. 全图加权
    5. 单点询问
    6. 联通块询问最大值
    7. 全图询问最大值

    解题思路

    把所有可能产生的联通块都变到一个区间里就好了

    考虑怎么排这个东西,可以先离线,每次加边的时候连接两个联通块根,这样跑出来的\(dfs\)序就是符合要求的。

    注意因为邻接表是按照加边的顺序倒着枚举的,所以要反过来加边。

    然后用线段树直接维护答案就好了

    时间复杂度\(O(n\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=3e5+10;
    struct qnode{
    	char op[3];
    	int x,y;
    }p[N];
    struct node{
    	int to,next;
    }a[N];
    int n,q,tot,cnt,ls[N],w[N],fa[N],l[N],r[N];
    struct SegTree{
    	int w[N<<2],lazy[N<<2];
    	void Downdata(int x){
    		if(!lazy[x])return;
    		w[x*2]+=lazy[x];lazy[x*2]+=lazy[x];
    		w[x*2+1]+=lazy[x];lazy[x*2+1]+=lazy[x];
    		lazy[x]=0;return;
    	}
    	void Change(int x,int L,int R,int l,int r,int val){
    		if(L==l&&R==r){w[x]+=val;lazy[x]+=val;return;}
    		int mid=(L+R)>>1;Downdata(x);
    		if(r<=mid)Change(x*2,L,mid,l,r,val);
    		else if(l>mid)Change(x*2+1,mid+1,R,l,r,val);
    		else Change(x*2,L,mid,l,mid,val),Change(x*2+1,mid+1,R,mid+1,r,val);
    		w[x]=max(w[x*2],w[x*2+1]);return;
    	}
    	int Ask(int x,int L,int R,int l,int r){
    		if(L==l&&R==r){return w[x];}
    		int mid=(L+R)>>1;Downdata(x);
    		if(r<=mid)return Ask(x*2,L,mid,l,r);
    		if(l>mid)return Ask(x*2+1,mid+1,R,l,r);
    		return max(Ask(x*2,L,mid,l,mid),Ask(x*2+1,mid+1,R,mid+1,r));
    	}
    }T;
    void addl(int x,int y){
    	if(x==y)return;
    	a[++tot].to=y;
    	a[tot].next=ls[x];
    	ls[x]=tot;return;
    }
    int find(int x)
    {return (fa[x]==x)?x:(fa[x]=find(fa[x]));}
    void dfs(int x){
    	l[x]=r[x]=++cnt;T.Change(1,1,n,cnt,cnt,w[x]);
    	for(int i=ls[x];i;i=a[i].next)
    		dfs(a[i].to);
    	return;
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%d",&w[i]),fa[i]=i;
    	scanf("%d",&q);
    	for(int i=1;i<=q;i++){
    		scanf("%s",p[i].op);
    		if(p[i].op[0]=='U'){
    			scanf("%d%d",&p[i].x,&p[i].y);
    			p[i].x=find(p[i].x);p[i].y=find(p[i].y);
    			if(p[i].x==p[i].y)continue;
    			if(p[i].x<p[i].y)swap(p[i].x,p[i].y);
    			fa[p[i].y]=p[i].x;
    		}
    		else if(p[i].op[0]=='A'&&p[i].op[1]=='1')
    			scanf("%d%d",&p[i].x,&p[i].y);
    		else if(p[i].op[0]=='A'&&p[i].op[1]=='2')
    			scanf("%d%d",&p[i].x,&p[i].y);
    		else if(p[i].op[0]!='F'||p[i].op[1]!='3')
    			scanf("%d",&p[i].x);
    	}
    	for(int i=q;i>=1;i--)
    		if(p[i].op[0]=='U')addl(p[i].x,p[i].y);
    	for(int i=1;i<=n;i++)
    		if(find(i)==i)dfs(i);
    	for(int i=1;i<=n;i++)fa[i]=i;
    	for(int i=1;i<=q;i++){
    		int x=p[i].x,y=p[i].y;
    		if(p[i].op[0]=='U'){
    			if(x==y)continue;
    			fa[y]=x;r[x]=r[y];
    		}
    		else if(p[i].op[0]=='A'&&p[i].op[1]=='1')
    			T.Change(1,1,n,l[x],l[x],y);
    		else if(p[i].op[0]=='A'&&p[i].op[1]=='2')
    			x=find(x),T.Change(1,1,n,l[x],r[x],y);
    		else if(p[i].op[0]=='A'&&p[i].op[1]=='3')
    			T.Change(1,1,n,1,n,x);
    		else if(p[i].op[0]=='F'&&p[i].op[1]=='1')
    			printf("%d\n",T.Ask(1,1,n,l[x],l[x]));
    		else if(p[i].op[0]=='F'&&p[i].op[1]=='2')
    			x=find(x),printf("%d\n",T.Ask(1,1,n,l[x],r[x]));
    		else printf("%d\n",T.Ask(1,1,n,1,n));
    	}
    	return 0;
    }
    
  • 相关阅读:
    CentOS7安装mysql
    centos 7 firewall(防火墙)开放端口/删除端口/查看端口
    CentOS7 FTP安装与配置
    处理nuget包太占用C盘
    windows下使用nginx
    SQL Server 设置新用户只能查看并访问特定数据库
    RPC框架
    RPC与REST
    Windows 环境下 Docker 使用及配置
    “远程调试监视器(MSVSMON.EXE)似乎没有在远程计算机上运行“的 解决方法
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14386284.html
Copyright © 2011-2022 走看看