BZOJ-4777 Switch Grass(最小生成樹+動態開點線段樹+可刪堆)
阿新 • • 發佈:2018-11-06
題意
給定一張
和節點,
條邊的無向圖,每個點有一個初始顏色,接下來有
個操作,每次操作會更改一個點的顏色,並詢問距離最近的不同色點對的最小距離,顏色上限不超過
且圖中至少有兩種顏色。
思路
首先答案肯定是連線兩個不同色點對的最短邊,而這個最短邊肯定在最小生成樹上。前者顯然成立,後者可以在
的過程中看出來。
那麼首先把圖縮成一棵最小生成樹並把它拎成一棵有根樹。對於每一個節點,我們維護它不同顏色子節點的資訊,由此達到不重複。
每次更新,對於一個節點
,它的顏色可能隨時會變,設之為
,每次的查詢就是連線顏色在區間
中兒子的邊的最小權值,我們不妨把邊權拉到子節點上方便處理。而同種顏色的子節點權值不一定相同。顏色權值兩維,不妨兩個資料結構套用:顏色要查詢區間,用線段樹維護,直接維護又會炸記憶體,改用動態開點;而權值要維護住很多值,還要支援刪除、插入、查詢最小值,
可以實現,但是可刪堆常數更小。
為了快速回答詢問,我們實時維護每個節點和它子節點之間的答案,用
可刪堆 維護這個陣列每次查詢最小值即可。
用資料結構解題首先要形成清晰的演算法流程,再套用合適的資料結構,當然模板要打熟,功底要深厚。
程式碼
#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=2e5+5;
const int M=4e5+5;
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],cost[maxm],tot;
Linked_list(){clear();}
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v,int w){to[++tot]=v,cost[tot]=w,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
struct DisjointSet
{
int fa[N],n;
void init(int _n){n=_n;FOR(i,1,n)fa[i]=i;}
int getfa(int k){return k==fa[k]?k:fa[k]=getfa(fa[k]);}
bool merge(int x,int y)
{
x=getfa(x),y=getfa(y);
if(x==y)return false;
fa[x]=y;
return true;
}
}DSU;
struct ErasableHeap
{
priority_queue<int,vector<int>,greater<int> >Q,E;
void push(int x){Q.push(x);}
void erase(int x){E.push(x);}
void modify(){while(!Q.empty()&&!E.empty()&&Q.top()==E.top())Q.pop(),E.pop();}
void pop(){modify();if(!Q.empty())Q.pop();}
int top(){modify();if(Q.empty())return 2e9;return Q.top();}
};
struct edge
{
int u,v,w;
bool operator <(const edge &_)const{return w<_.w;}
}E[M];
Linked_list<N,N<<1>G;
ErasableHeap ans;
struct node{int lson,rson,miner,id;};
struct SegmentTree
{
ErasableHeap st[N<<2];
node nd[N*40];
int rt[N<<1],tot,ID;
void build(){memset(rt,0,sizeof(rt));tot=ID=0;nd[0].miner=2e9;}
void create(int &k){if(k==0)nd[k=++tot]=(node){0,0,2e9,0};}
void add_up(int k){nd[k].miner=min(nd[nd[k].lson].miner,nd[nd[k].rson].miner);}
void update(int &k,int x,int A,bool B,int l,int r)
{
create(k);
if(l==r)
{
if(nd[k].id==0)nd[k].id=++ID;
if(B)st[nd[k].id].push(A);
else st[nd[k].id].erase(A);
nd[k].miner=st[nd[k].id].top();
return;
}
int mid=l+r>>1;
if(x<=mid)update(nd[k].lson,x,A,B,l,mid);
else update(nd[k].rson,x,A,B,mid+1,r);
add_up(k);
}
int query(int &k,int L,int R,int l,int r)
{
if(!k)return 2e9;
if(L<=l&&r<=R)return nd[k].miner;
int mid=l+r>>1;
if(R<=mid)return query(nd[k].lson,L,R,l,mid);
else if(L>mid)return query(nd[k].rson,L,R,mid+1,r);
else return min(query(nd[k].lson,L,R,l,mid),query(nd[k].rson,L,R,mid+1,r));
}
}ST;
int fa[N],p[N],minson[N],c[N],n,m,K,Q;
void Kruskal()
{
DSU.init(n);
sort(E+1,E+1+m);
FOR(i,1,m)if(DSU.merge(E[i].u,E[i].v))
{
G.add(E[i].u,E[i].v,E[i].w);
G.add(E[i].v,E[i].u,E[i].w);
}
}
int getMin(int u)
{
if(c[u]==1)return ST.query(ST.rt[u],c[u]+1,K,1,K);
else if(c[u]==K)return ST.query(ST.rt[u],1,c[u]-1,1,K);
else return min(ST.query(ST.rt[u],1,c[u]-1,1,K),ST.query(ST.rt[u],c[u]+1,K,1,K));
}
void dfs(int u,int f)
{
fa[u]=f;
EOR(i,G,u)
{
int v=G.to[i],w=G.cost[i];
if(v==f)continue;
p[v]=w;
ST.update(ST.rt[u],c[v],p[v],1,1,K);
dfs(v,u);
}
minson[u]=getMin(u);
ans.push(minson[u]);
}
int main()
{
ST.build();
scanf("%d%d%d%d",&n,&m,&K,&Q);
FOR(i,1,m)scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
FOR(i,1,n)scanf("%d",&c[i]);
Kruskal();
dfs(1,0);
while(Q--)
{
int x,y;
scanf("%d%d",&x,&y);
if(fa[x])
{
ST.update(ST.rt[fa[x]],c[x],p[x],0,1,K);
c[x]=y;
ST.update(ST.rt[fa[x]],c[x],p[x],1,1,K);
ans.erase(minson[fa[x]]);
minson[fa[x]]=getMin(fa[x]);
ans.push(minson[fa[x]]);
}
else c[x]=y;
ans.erase(minson[x]);
minson[x]=getMin(x);
ans.push(minson[x]);
printf("%d\n",ans.top());
}
return