1. 程式人生 > >【整體二分+線段樹合併+tarjan】LGP5163 WD與地圖

【整體二分+線段樹合併+tarjan】LGP5163 WD與地圖

【題目】
原題地址
給定一幅有向圖,支援三種操作:

  • 刪去一條邊
  • 給一個點權值增加 x x
  • x x 點所在強連通分量權值前 k
    k
    大的和。
    n , m , q 1
    0 5 n,m,q\leq 10^5

【解題思路】
我好像不是很會做啊(其實是自己s*了)。

首先倒序操作,那麼刪邊可以變成加邊。一個 naive \text{naive}

的做法是我們對每個點維護一棵權值線段樹,每次增加一條邊 ( x , y ) (x,y) ,我們就從 y y x x 跑縮邊,將全部權值線段樹合併一下。詢問就在權值線段樹上二分即可。
這樣做的複雜度是 O ( m q + n log n + q log n ) O(mq+n\log n+q\log n) 的。

實際上瓶頸在於無法快速知道哪條邊在什麼時候被縮起來,否則我們可以按時間進行線段樹合併。

考慮到邊被縮起來的時間滿足可二分性,於是我們用整體二分的思想:將出現時間 m i d \leq mid 的邊全部加入圖中跑縮邊,我們可以知道在 m i d \leq mid 時間內有哪些邊被縮掉。同時我們還需要用可持久化陣列(霧)來維護一下是否在一個強連通分量內以及 d f n dfn (這樣不用重複遍歷邊了)。每條邊在每個分治深度下貢獻一次,那麼複雜度就是 O ( n log n α ( n ) ) O(n\log n\cdot \alpha (n)) 了。

這種知識點題的思考過程:欸我要一個tarjan,碼碼碼;欸我要一個線段樹合併,碼碼碼;欸我要一個整體二分,碼碼碼。然後就200+行了。

【參考程式碼】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
const int N=4e5+10,M=N*50;
int tn,n,m,Q,cnt,val[N];
vector<ll>ans;
set<pii>E;
ll res[N];

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(ll x)
	{
		if(x>9) write(x/10);
		putchar(x%10^48);
	}
	void writeln(ll x){write(x);puts("");}
}
using namespace IO;

namespace Segment
{
	int sz,ls[M],rs[M],cnt[M],rt[N];
	ll sum[M];
	void update(int &x,int l,int r,int p,int v)
	{
		if(!x)x=++sz;cnt[x]+=v;sum[x]+=(ll)p*v;
		if(l==r) return;
		int mid=(l+r)>>1;
		if(p<=mid) update(ls[x],l,mid,p,v);
		else update(rs[x],mid+1,r,p,v);
	}
	ll query(int x,int l,int r,int p)
	{
		if(p>=cnt[x]) return sum[x];
		if(l==r) return sum[x]/cnt[x]*p;
		int mid=(l+r)>>1;
		if(cnt[rs[x]]>=p) return query(rs[x],mid+1,r,p);
		else return query(ls[x],l,mid,p-cnt[rs[x]])+sum[rs[x]];
	}
	int Tmerge(int x,int y)
	{
		if(!x || !y) return x+y;
		cnt[x]+=cnt[y];sum[x]+=sum[y];
		ls[x]=Tmerge(ls[x],ls[y]);
		rs[x]=Tmerge(rs[x],rs[y]);
		return x;
	}
}
using Segment::update;
using Segment::query;
using Segment::Tmerge;
using Segment::rt;

namespace DSU
{
	int fa[N];
	int findf(int x){return fa[x]==x?x:fa[x]=findf(fa[x]);}
	void merge(int x,int y)
	{
		x=findf(x);y=findf(y);
		if(x==y) return;
		if(x>y) swap(x,y);
		fa[x]=y;
	}
}
using namespace DSU;

namespace Graph
{
	int ind,tot;
	int low[N],dfn[N],ins[N],head[N];
	stack<int>st;
	struct edge
	{
		int u,v,t;
		edge(int _u=0,int _v=0,int _t=0):u(_u),v(_v),t(_t){}
	}g[N],cpy[N];
	vector<edge>mp[N];
	struct Tway
	{
		int v,nex;
		Tway(int _v=0,int _nex=0):v(_v),nex(_nex){}
	}e[N];
	void addedge(int u,int v){e[++tot]=Tway(v,head[u]);head[u]=tot;}
	void clear(int x){low[x]=dfn[x]=ins[x]=0;}
	void tarjan(int x)
	{
		dfn[x]=low[x]=++ind;ins[x]=1;st.push(x);
		for(int i=head[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
			else if(ins[v]) low[x]=min(low[x],dfn[v]);
		}
		if(low[x]^dfn[x]) return;
		for(int t=0;t^x;t=st.top(),st.pop()) merge(st.top(),x),ins[st.top()]=0;
	}
}
using Graph::addedge;
using Graph::tarjan;
using Graph::dfn;
using Graph::edge;
using Graph::mp;
using Graph::g;
using Graph::cpy;

namespace Divide_Conquer
{
	pii arr[N];
	void solve(int l,int r,int L,int R)
	{
		//cerr<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
		if(l==r)
		{ 
			for(int i=L;i<=R;++i) mp[l].pb(g[i]);
			return;
		}
		if(L==R)
		{
			mp[(findf(g[L].u)==findf(g[L].v))?g[L].t:Q+1].pb(g[L]);
			return;
		}
		int mid=(l+r)>>1,c=0;
		for(int i=L;i<=R;++i) if(g[i].t<=mid)
		{
			int u=findf(g[i].u),v=findf(g[i].v);
			arr[++c]=mkp(u,v);Graph::head[u]=Graph::head[v]=0;
		}
		Graph::tot=0;
		for(int i=1;i<=c;++i)
		{
			int u=arr[i].fi,v=arr[i].se;addedge(u,v);
			Graph::clear(u);Graph::clear(v);
		}
		//cerr<<c<<endl;
		//if(r==12500) exit(0);
		Graph::ind=0;
		while(!Graph::st.empty()) Graph::st.pop();
		for(int i=1;i<=c;++i)
		{
			if(!dfn[arr[i].fi]) tarjan(arr[i].fi);
			if(!dfn[arr[i].se]) tarjan(arr[i].se);
		}
		int tl=0,tr=L,t=1;vector<pii> cp;
		for(int i=L;i<=R;++i)
		{
			int fg=0;
			if(g[i].t<=mid)
			{
				int u=arr[t].fi,v=arr[t].se;++t;
				if(findf(u)==findf(v)) fg=1;
				cp.pb(mkp(u,fa[u]));cp.pb(mkp(v,fa[v]));
			}
			if(fg) g[tr++]=g[i]; else cpy[++tl]=g[i];
		}
		for(int i=tr;i<=R;++i) g[i]=cpy[i-tr+1];
		for(int i=1;i<=c;++i) fa[arr[i].fi]=arr[i].fi,fa[arr[i].se]=arr[i].se;
		if(L<tr) solve(l,mid,L,tr-1);
		for(vector<pii>::iterator it=cp.begin();it!=cp.end();++it) fa[(*it).fi]=(*it).se;
		//exit(0);
		if(tr<=R) solve(mid+1,r,tr,R);
	}
}

struct Tquery
{
	int t,a,b;
	Tquery(int _t=0,int _a=0,int _b=0):t(_t),a(_a),b(_b){}	
}