1. 程式人生 > 實用技巧 >A All-Star Game(維護連通分量大小,線段樹+可撤回並查集)

A All-Star Game(維護連通分量大小,線段樹+可撤回並查集)

題:https://ac.nowcoder.com/acm/contest/5673/A

題意:有n個球員m個粉絲,每個粉絲可以是若干個球員的粉絲,現要準備一場比賽,問最少安排多少個球員讓所有粉絲都想看比賽(想看比賽得是有粉絲喜愛的球迷登場),還要提供詢問操作:粉絲成為球員的粉絲,粉絲撤銷成為球員的粉絲

分析:將粉絲開始成為球員粉絲到撤銷成為球員粉絲定成一個時間區間,總共有q個詢問時間大小就為q,這個q剛好可以表示上線段樹上的n,那麼處理輸出就是在葉子節點進行操作,對於時間區間的處理用並查集維護其連通性,其中用棧記錄曾經的相關值,在跳出這個時間區間的時候直接按出棧順序返回恢復以前的值即可

#include<bits/stdc++.h>
using
namespace std; #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r #define pb push_back #define ull unsigned long long #define pii pair<int,int> #define MP make_pair typedef long long ll; const ll INF=1e18; const int M=5e5+6; int Rank[M],fa[M],lst[M*10],red[M*10]; struct node{
int u,v; }e[M*10]; map<pii,int>mp; vector<int>tr[M<<2]; int ans,sum,top,n,m,q,tot; void update(int L,int R,int id,int root,int l,int r){ if(L<=l&&r<=R){ tr[root].pb(id); ///cout<<id<<"!!"<<endl; return; } int midd=(l+r)>>1
; if(L<=midd)update(L,R,id,lson); if(R>midd)update(L,R,id,rson); } struct Node{ int fau,fav,rku,rkv,sum,ans; }sta[M*10]; int Find(int x){ return x==fa[x]?x:Find(fa[x]); } void Union(int x,int y){ top++; x=Find(x),y=Find(y); sta[top].fau=x,sta[top].fav=y; sta[top].rku=Rank[x],sta[top].rkv=Rank[y]; sta[top].sum=sum,sta[top].ans=ans; if(x==y)return; if(x<=n&&y>n){///粉絲y沒有偶像 sum++; if(Rank[x]==1)///偶像x沒有粉絲 ans++; } else{ if(Rank[x]!=1&&Rank[y]!=1)///粉絲z有偶像y,現在z喜歡的偶像x也原本有粉絲 ans--; } if(Rank[x]<Rank[y])swap(x,y); if(Rank[x]==Rank[y])Rank[x]++; fa[y]=x; } ///由於棧記錄了之前步驟,所以依次撤銷操作即可 void stapop(){ fa[sta[top].fau]=sta[top].fau; fa[sta[top].fav]=sta[top].fav; Rank[sta[top].fau]=sta[top].rku; Rank[sta[top].fav]=sta[top].rkv; sum=sta[top].sum,ans=sta[top].ans; top--; } void query(int root,int l,int r){ for(auto i:tr[root]) Union(e[i].u,e[i].v); if(l==r){ if(sum!=m)///沒有所有粉絲都能看到 puts("-1"); else printf("%d\n",ans); } else{ int midd=(l+r)>>1; query(lson); query(rson); } int len=tr[root].size(); while(len--){ stapop(); } } int main(){ scanf("%d%d%d",&n,&m,&q); ///球員1~n,粉絲n+1~n+m for(int i=1;i<=n+m;i++){ Rank[i]=1; fa[i]=i; } for(int i=1;i<=n;i++){ int k; scanf("%d",&k); while(k--){ int v; scanf("%d",&v),v+=n; if(!mp.count(MP(i,v))){ mp[MP(i,v)]=++tot; e[tot]=node{i,v}; } lst[tot]=1; } } for(int i=1;i<=q;i++){ int u,v; scanf("%d%d",&v,&u); v+=n; if(!mp.count(MP(u,v))){ mp[MP(u,v)]=++tot; e[tot]=node{u,v}; } int id=mp[MP(u,v)]; if(lst[id]==0) lst[id]=i; else{ red[id]=i-1; if(i!=1) update(lst[id],red[id],id,1,1,q); lst[id]=red[id]=0; } } ///把到最後也沒有消除的邊人為地加上在q時刻結束 for(int i=1;i<=tot;i++) if(lst[i]){ red[i]=q; update(lst[i],red[i],i,1,1,q); } query(1,1,q); return 0; }
View Code