zoukankan      html  css  js  c++  java
  • 【题解】P5163 WD与地图 (这题极好)

    【题解】P5163 WD与地图 (这题极好)

    题目大意

    初始有一个有向图,现在要你支持以下操作

    • 删除一条边
    • 把一个点点权修改下
    • 输出一个点所在的SCC的最大k个点权和

    (nle 1e5,m,q le 2e5,)

    删边不好处理,先时间倒流下,问题变成了支持加入一条边。

    如果图是无向图,这题就很好做(并查集+treap/线段树合并),因为每条边都把两个端点立马变成了一个SCC了。而有向图难处理就难在可能很多条边会在最后一条关键边加入时,同时使得很多点变成一个SCC。

    考虑我们让"很多边"和"关键边"同时加入,这样显然是不影响答案的(SCC没有改变)。

    图的连通性是满足二分性的,考虑对于每条边,二分出这样一个最早时刻(t),使得这条边的两个端点在一个SCC里。然后我们在(t)时刻时把这条边当做无向边加入。

    这样的复杂度无法接受,然而多个元素共用一个( m{check})可以整体二分。可撤销并查集维护SCC即可。

    得到这些之后,再套用无向图的做法即可

    //@winlere
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<vector>
    
    using namespace std;  typedef long long ll;
    inline int qr(){
    	int ret=0,f=0,c=getchar();
    	while(!isdigit(c)) f|=c==45,c=getchar();
    	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
    	return f?-ret:ret;
    }
    const int maxn=2e5+5;
    int n,m,q,w[maxn],T[maxn],usd[maxn],len;
    pair<int,int> e[maxn];
    struct{int op,a,b;}que[maxn];
    ll ans[maxn];
    vector<int>sav;
    
    int getVal(int x){
    	return lower_bound(sav.begin(),sav.end(),x)-sav.begin();
    }
    
    namespace getTime{
    	namespace BCJ{
    		int r[maxn],siz[maxn],stk[maxn],top;
    		int Find(int x){return x==r[x]?x:Find(r[x]);}
    		int Merge(int x,int y){
    			x=Find(x),y=Find(y);
    			if(x==y) return 0;
    			if(siz[x]>siz[y]) swap(x,y);
    			r[x]=y; siz[y]+=siz[x]; stk[++top]=x;
    			return 1;
    		}
    		void Undo(int cnt){
    			int temp;
    			while(cnt-->0&&top)
    				temp=stk[top--],siz[r[temp]]-=siz[temp],r[temp]=temp;
    		}
    		void init(){for(int t=1;t<=n;++t)r[t]=t,siz[t]=1;}
    	}using namespace BCJ;
    	namespace Tarjan{
    		vector<int>e[maxn],ve;
    		int dfn[maxn],low[maxn],T,stk[maxn],in[maxn],top,cnt;
    		void add(int fr,int to){
    			if(fr==to) return;
    			ve.push_back(fr); ve.push_back(to);
    			e[fr].push_back(to);
    		}
    		void clear(){
    			for(auto t:ve) e[t].clear(),dfn[t]=low[t]=0;
    			ve.clear(); T=0; cnt=0;
    		}
    		void dfs(int now){
    			dfn[now]=low[now]=++T; stk[++top]=now; in[now]=1;
    			for(auto t:e[now]){
    				if(!dfn[t]) dfs(t),low[now]=min(low[now],low[t]);
    				if(dfn[t]&&in[t]) low[now]=min(low[now],dfn[t]);
    			}
    			if(low[now]==dfn[now]){
    				int temp;
    				do
    					cnt+=Merge(temp=stk[top--],now),in[temp]=0;
    				while(temp!=now);
    			}
    		}
    		int init(){
    			for(auto t:ve)
    				if(!dfn[t])
    					dfs(t);
    			return cnt;
    		}
    	}
    	vector<pair<int,int>> getEdge(int l,int r){
    		vector< pair<int,int> > ret;
    		if(l==0){
    			for(int t=1;t<=m;++t)
    				if(!usd[t])
    					ret.push_back(e[t]);
    		}
    		for(int t=l;t<=r;++t)
    			if(que[t].op==1)
    				ret.push_back({que[t].a,que[t].b});
    		return ret;
    	}
    	void solve(int l,int r,vector<int>E){
    		if(l>=r||E.empty()){
    			for(auto t:E) T[t]=l;
    			return;
    		}
    		int mid=(l+r)>>1,cnt=0;
    		Tarjan::clear();
    		for(auto t:E)
    			if(usd[t]<=mid)
    				Tarjan::add(Find(e[t].first),Find(e[t].second));
    		cnt=Tarjan::init();
    		vector<int>lef,rgt;
    		for(auto t:E)
    			if(Find(e[t].first)==Find(e[t].second)&&usd[t]<=mid)
    				lef.push_back(t);
    			else rgt.push_back(t);
    		solve(mid+1,r,rgt);
    		Undo(cnt);
    		solve(l,mid,lef);
    	}
    	void init(){
    		BCJ::init();
    		vector<int>ve;
    		for(int t=1;t<=m;++t) ve.push_back(t);
    		solve(0,q+1,ve);
    	}
    }
    
    namespace getAns{
    	struct EDGE{
    		int u,v,T;
    		bool operator < (EDGE x)const{return T<x.T;}
    	}edge[maxn];
    	struct NODE{
    		int ls,rs,val,cnt,siz;
    	}seg[maxn<<6];	
    	ll sum[maxn<<6];
    	int cnt,r[maxn],rt[maxn];
    	void pp(int pos){
    		seg[pos].siz=seg[pos].cnt+seg[seg[pos].ls].siz+seg[seg[pos].rs].siz;
    		sum[pos]=sum[seg[pos].ls]+sum[seg[pos].rs]+1ll*seg[pos].val*seg[pos].cnt;
    	}
    #define mid ((l+r)>>1)
    #define lef l,mid,seg[pos].ls
    #define rgt mid+1,r,seg[pos].rs
    	void build(int p,int l,int r,int&pos){
    		if(l>p||r<p) return;
    		if(!pos) pos=++cnt;
    		if(l==r) return sum[pos]=sav[p],seg[pos].val=sav[p],seg[pos].cnt=seg[pos].siz=1,void();
    		if(p<=mid) build(p,lef);
    		if(p>mid) build(p,rgt);
    		pp(pos);
    	}
    	void upd(int p,int delta,int l,int r,int&pos){
    		if(p<l||r<p) return;
    		if(!pos) pos=++cnt;
    		if(l==r){
    			seg[pos].cnt+=delta; seg[pos].siz+=delta;
    			seg[pos].val=sav[p];
    			sum[pos]=1ll*seg[pos].cnt*seg[pos].val;
    			return;
    		}
    		if(p<=mid) upd(p,delta,lef);
    		else upd(p,delta,rgt);
    		pp(pos);
    	}
    	int Merge(int l,int r,int t1,int t2){
    		if(!t1||!t2) return t1|t2;
    		if(t1==t2) cerr<<"Merge_same"<<t1<<endl;
    		if(seg[t1].val!=seg[t2].val) return cerr<<"wrong value!"<<endl,0;
    		seg[t1].cnt+=seg[t2].cnt;
    		seg[t1].siz+=seg[t2].siz;
    		seg[t1].ls=Merge(l,mid,seg[t1].ls,seg[t2].ls);
    		seg[t1].rs=Merge(mid+1,r,seg[t1].rs,seg[t2].rs);
    		pp(t1);
    		return t1;
    	}
    	ll sumK(int k,int l,int r,int pos){
    		if(!pos||!k) return 0;
    		if(l==r) return 1ll*seg[pos].val*min(seg[pos].cnt,k);
    		if(k>=seg[seg[pos].rs].siz) return sum[seg[pos].rs]+sumK(k-seg[seg[pos].rs].siz,lef);
    		return sumK(k,rgt);
    	}
    	int Find(int x){return x==r[x]?x:r[x]=Find(r[x]);}
    #undef mid
    #undef lef
    #undef rgt
    	void Merge(int u,int v){
    		u=Find(u); v=Find(v);
    		if(u==v) return;
    		rt[v]=rt[u]=Merge(1,len,rt[u],rt[v]);
    		r[u]=v;
    	}
    	void init(){
    		cnt=n;
    		for(int t=1;t<=n;++t) build(getVal(w[t]),1,len,rt[t]),r[t]=t;
    		for(int t=1;t<=m;++t)
    			edge[t]={e[t].first,e[t].second,T[t]};
    		sort(edge+1,edge+m+1);
    		for(int t=1,j=0;t<=q;++t){
    			if(que[t].op==1) continue;
    			while(j+1<=m&&edge[j+1].T<=t) ++j,Merge(edge[j].u,edge[j].v);
    			if(que[t].op==2)
    				upd(getVal(w[que[t].a]),-1,1,len,rt[Find(que[t].a)]),w[que[t].a]-=que[t].b,upd(getVal(w[que[t].a]),1,1,len,rt[Find(que[t].a)]);
    			if(que[t].op==3) ans[t]=sumK(que[t].b,1,len,rt[Find(que[t].a)]);
    		}
    	}
    }
    
    signed main(){
    	n=qr(); m=qr(); q=qr();	
    	for(int t=1;t<=n;++t) w[t]=qr(),sav.push_back(w[t]);
    	for(int t=1;t<=m;++t) e[t].first=qr(),e[t].second=qr();
    	sort(e+1,e+m+1);
    	for(int t=1;t<=q;++t) {
    		que[t].op=qr(),que[t].a=qr(),que[t].b=qr();
    		if(que[t].op==2) w[que[t].a]+=que[t].b,sav.push_back(w[que[t].a]);
    	}
    	reverse(que+1,que+q+1);
    	for(int t=1;t<=q;++t){
    		if(que[t].op==1) usd[lower_bound(e+1,e+m+1,(pair<int,int>){que[t].a,que[t].b})-e]=t;
    	}
    	sav.push_back(-1);
    	sort(sav.begin(),sav.end());
    	sav.resize(unique(sav.begin(),sav.end())-sav.begin());
    	len=sav.size()-1;
    	getTime::init();
    	getAns::init();
    	for(int t=1;t<=q;++t)
    		if(que[q-t+1].op==3)
    			printf("%lld
    ",ans[q-t+1]);
    	return 0;
    }
    
    
    
  • 相关阅读:
    SQL Server 2005 中的同义词
    SQL SERVER 2005中同义词实例
    聚集索引和非聚集索引(整理)
    linux kernel中timer的使用
    linux命令: patch
    win7(64位)php5.5-Apache2.4-mysql5.6环境安装
    tasklet和工作队列
    linux串口编程(c)
    Notepad++中Windows,Unix,Mac三种格式
    centos7/redhat7 将网卡名字改成eth样式的方法
  • 原文地址:https://www.cnblogs.com/winlere/p/12933613.html
Copyright © 2011-2022 走看看