【CF487E】 Tourists 簡易題解

題目大意

       \ \ \ \ \ \ \, 給你 nn 個點和 mm條邊的無向圖,沒有自環,沒有重邊,每個點上面有點權。

       \ \ \ \ \ \ \, 每次可能有兩種操作:修改一個點的點權,或者詢問兩個點之間的路徑上最小可能的點權是多少。

想法

       \ \ \ \ \ \ \,有一個很顯然的貪心想法,詢問的時候肯定優先走較小權值路徑,也就是在有分叉(點雙)的時候走較小權值的那一側,而只有可能最小的會造成貢獻。

       \ \ \ \ \ \ \,所以說我們可以嘗試將每個點雙之間建一個堆,記錄最小的點權。

       \ \ \ \ \ \ \,也就是建出一棵圓方樹,方點上面一個堆,記錄與他向連的圓點的權值最小值。

       \ \ \ \ \ \ \,每次詢問就只需要考慮經過唯一路徑上面 圓點點權方點堆頂 的最小值就行了,這個可以用樹鏈剖分搞。

       \ \ \ \ \ \ \,然後考慮修改,便是修改與這個 圓點 相連的方點上面的堆就好了,刪除原來的點權,加入新點權。但是這樣子我們不能保證其複雜度,要是圓方樹建成一個菊花圖就會 TT 飛了。

       \ \ \ \ \ \ \,我們再考慮一下,我們樹鏈剖分往上跳的時候,其實已經計算過 走過的方點 的父親了(圓點),會有計算重複的地方。所以說 方點的堆 裏面不需要記錄其父親的權值,換句話說,對於一個圓點,他的值只需要被他的 方點父親 的堆記錄,這樣子每次修改,只需要修改他父親的,加上線段樹和堆的複雜度也不過一次 O(logn)O(\log n)

       \ \ \ \ \ \ \,但是每次詢問的是兩個圓點,其LCALCA有可能是方點,這個時候少計算了一個 方點LCALCA 的父親,需要注意加入判斷一下。(第一個樣第一次例詢問輸出爲2有可能就是這樣死的)

       \ \ \ \ \ \ \,然後就是 圓方樹+樹鏈剖分+線段樹+可刪堆 套模板了,可刪堆我是用 fhq_Treapfhq\_Treap 實現的,所以說代碼比較醜。

       \ \ \ \ \ \ \,預處理複雜度O(nlogn)O(n\log n),修改操作複雜度O(logn)O(\log n),詢問操作複雜度O(log2n)O(\log^2 n)

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<map>
#include<set>
using namespace std;
const int inf=0x3f3f3f3f;
const double eps=1e-10;
const double pi=acos(-1.0);
//char buf[1<<15],*S=buf,*T=buf;
//char getch(){return S==T&&(T=(S=buf)+fread(buf,1,1<<15,stdin),S==T)?0:*S++;}
inline int read(){
	int x=0,f=1;char ch;ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=0;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch&15);ch=getchar();}
	if(f)return x;else return -x;
}
const int N=4e5+200;
vector<int> G[N],E[N];
void add_E(int x,int y){E[x].push_back(y);E[y].push_back(x);}
void add_G(int x,int y){G[x].push_back(y);G[y].push_back(x);}
int sta[N],top,Size;
int tim,dfn[N],low[N];
void tarjan(int u){
  	dfn[u]=low[u]=++tim;
  	sta[++top]=u;
  	for(auto v:E[u]){
    	if(dfn[v]) low[u]=min(low[u],dfn[v]);
    	else{
      		tarjan(v),low[u]=min(low[u],low[v]);
      		if(low[v]>=dfn[u]){
        		++Size;int p;
        		while((p=sta[top])!=v)add_G(p,Size),top--;
        		p=sta[top],add_G(p,Size),top--;
        		add_G(u,Size);
      		}
    	}
  	}
}

struct node{int ls,rs,key,size,val;}T[N<<1];
#define lson T[rt].ls
#define rson T[rt].rs
int node_cnt;
queue<int>Rub;
int newnode(int x){
	int rt;
	if(!Rub.empty())rt=Rub.front(),Rub.pop();
	else rt=++node_cnt;
	lson=rson=0;
	T[rt].key=rand();T[rt].val=x;T[rt].size=1;
	return rt;
}
struct fhq_Treap{
	int root;
	void pushup(int rt){T[rt].size=T[lson].size+T[rson].size+1;}
	int merge(int a,int b){
		if(!a||!b)return a|b;
		if(T[a].key<T[b].key){T[a].rs=merge(T[a].rs,b);pushup(a);return a;}
		else{T[b].ls=merge(a,T[b].ls);pushup(b);return b;}
	}
	void split(int rt,int x,int &a,int &b){
		if(!rt){a=b=0;return;}
		if(T[rt].val<=x){a=rt;split(rson,x,rson,b);}
		else{b=rt;split(lson,x,a,lson);}
		pushup(rt);
	}
	void Insert(int x){
		int a,b;
		split(root,x,a,b);
		int rt=newnode(x);
		root=merge(merge(a,rt),b);
	}
	void Delete(int x){
		int a,b,c;
		split(root,x,a,c);
		split(a,x-1,a,b);
		b=merge(T[b].ls,T[b].rs);
		root=merge(merge(a,b),c);
	}
	int Min(){
		int rt=root;
		while(lson)rt=lson;
		return T[rt].val;
	}
}Treap[N];

int pos[N],w[N];
struct Segment_Tree{
  	#define Lson l,mid,rt<<1
  	#define Rson mid+1,r,rt<<1|1
  	int sum[N<<2];
  	void pushup(int rt){sum[rt]=min(sum[rt<<1],sum[rt<<1|1]);}
  	void build(int l,int r,int rt){
  		if(l==r){sum[rt]=w[pos[l]];return;}
  		int mid=(l+r)>>1;
  		build(Lson);build(Rson);
  		pushup(rt);
	}
  	void Update(int id,int c,int l,int r,int rt){
    	if(l==r){sum[rt]=c;return;}
    	int mid=(l+r)>>1;
    	if(id<=mid)Update(id,c,Lson);
    	else Update(id,c,Rson);
		pushup(rt);
  	}
  	int Query(int L,int R,int l,int r,int rt){
    	if(L<=l&&r<=R)return sum[rt];
    	int ret=1e9,mid=(l+r)>>1;
    	if(L<=mid)ret=min(ret,Query(L,R,Lson));
    	if(R>mid)ret=min(ret,Query(L,R,Rson));
    	return ret;
  	}
}Seg;

int n,m,Q;
struct Tree_Chain_Dissection{
	int idx[N];
	int deep[N],fa[N],son[N],size[N];
	int cnt,top[N];
	int dfs1(int u,int f,int dep){
		deep[u]=dep;fa[u]=f;size[u]=1;
		Treap[fa[u]].Insert(w[u]);
		int maxson=-1;
	  	for(auto v:G[u])if(v!=f){
		  	size[u]+=dfs1(v,u,dep+1);
		  	if(size[v]>maxson)maxson=size[v],son[u]=v;
		}
		return size[u];
	}
	void dfs2(int u,int topf){
		idx[u]=++cnt;top[u]=topf;
		pos[cnt]=u;
		if(!son[u])return;
		dfs2(son[u],topf);
	  	for(auto v:G[u])if(!idx[v])dfs2(v,v);
	}
	void init(){
		dfs1(1,0,1);
		dfs2(1,1);
	  	for(int i=n+1;i<=Size;++i)w[i]=Treap[i].Min();
		Seg.build(1,Size,1);
	}
	void Update(int u,int val){
	  	if(fa[u]){
	    	Treap[fa[u]].Delete(w[u]);
	    	Treap[fa[u]].Insert(val);
	    	Seg.Update(idx[fa[u]],Treap[fa[u]].Min(),1,Size,1);
	  	}
	  	w[u]=val;
		Seg.Update(idx[u],val,1,Size,1);
	}
	int Query(int x,int y){
	  	int ans=inf;
	  	while(top[x]!=top[y]){
	    	if(deep[top[x]]<deep[top[y]])swap(x,y);
	    	ans=min(ans,Seg.Query(idx[top[x]],idx[x],1,Size,1));
	    	x=fa[top[x]];
	  	}
	 	if(deep[x]>deep[y])swap(x,y);
	  	ans=min(ans,Seg.Query(idx[x],idx[y],1,Size,1));
    	if(x<=n)return ans;
    	else return min(ans,w[fa[x]]);
	}
}TCD;

char op[2];
int a,b;

int main()
{
	srand(time(NULL));
	Size=n=read();m=read();Q=read();w[0]=inf;
  	for(int i=1;i<=n;++i)w[i]=read();
  	for(int i=1,a,b;i<=m;++i)
	a=read(),b=read(),add_E(a,b);
  	tarjan(1);
  	TCD.init();
  	while(Q--){
    	scanf("%s",op);a=read(),b=read();
    	if(op[0]=='C')TCD.Update(a,b);
    	if(op[0]=='A')printf("%d\n",TCD.Query(a,b));
  	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章