1. 程式人生 > >The 2017 ACM-ICPC Asia Beijing Regional Contest C - Graph

The 2017 ACM-ICPC Asia Beijing Regional Contest C - Graph

題面

題意

給出一張圖,每次詢問給出l,r,定義序號在l,r之間的點為安全的,求只經過安全點就可以互相到達的點對數。

做法

首先題目不難轉化為求所有聯通塊的貢獻和,每個聯通塊的貢獻為 ( s 1 )

s / 2 (s-1)*s/2 ,s表示該聯通塊的大小。
因為是聯通塊,所以我們考慮有哪些邊在塊內,然後每條邊記錄兩條(u->v,v->u),根據左端點排序,這樣序號在一段區間內的點所對應的邊就是一段區間內出現兩次的邊(兩個方向各出現一次)。
然後我們就可以用莫隊+可撤銷並查集來做,我們可以發現經過莫隊的預處理後,如果詢問的左右端點在同一個塊內,則直接暴力求解,否則每一塊中的詢問情況應該如圖:
在這裡插入圖片描述

每條橫著的線段表示一個詢問,然後發現左端點參差不齊,而右端點單調遞增。
因此在處理詢問時,可以先加入右邊的邊,然後再加左邊的邊,求出答案後再撤銷左邊的所有邊,這樣每次刪掉的邊就是剛加入的邊,就能用帶撤銷並查集維護了。

程式碼

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
#define P pair<ll,ll>
#define mp make_pair
#define fi first #define se second #define N 200010 using namespace std; ll T,n,m,Q,s,ks,fa[N],size[N],ans,an[N],cnt[N],a[N],b[N],fl[N],fr[N],top,tail; P sta[N]; struct Bn { ll u,v,id; bool operator < (const Bn &t) const { return u<t.u; } }bn[N<<1]; struct Que { ll l,r,id; bool operator < (const Que &u) const { if(fl[l]/s!=fl[u.l]/s) return fl[l]/s<fl[u.l]/s; return fr[r]<fr[u.r]; } }que[N]; inline ll calc(ll u){return u*(u-1)/2;} ll ff(ll u){return u==fa[u]?u:ff(fa[u]);} inline void mg(ll u,ll v) { if(size[u]>size[v]) swap(u,v); sta[++top]=mp(u,v); ans+=calc(size[u]+size[v])-calc(size[u])-calc(size[v]); size[v]+=size[u]; fa[u]=v; } inline void spl(ll u,ll v) { size[v]-=size[u]; fa[u]=u; ans-=calc(size[u]+size[v])-calc(size[u])-calc(size[v]); } inline void add(ll u) { cnt[bn[u].id]++; ll p=ff(bn[u].u),q=ff(bn[u].v); if(cnt[bn[u].id]==2&&p!=q) { mg(p,q); } } inline void clear() { for(;top!=tail;top--) { spl(sta[top].fi,sta[top].se); } } inline ll solve(ll u,ll v) { ll i,j,res; for(i=u;i<=v;i++) add(i); res=ans; clear(); for(i=u;i<=v;i++) cnt[bn[i].id]--; return res; } int main() { ll i,j,k,p,q,l,r; cin>>T; while(T--) { memset(fr,0,sizeof(fr)); scanf("%lld%lld%lld",&n,&m,&Q); s=sqrt(m<<1),ks=(m<<1)/s; for(i=1;i<=m;i++) { scanf("%lld%lld",&p,&q); bn[(i<<1)-1].u=p; bn[(i<<1)-1].v=q; bn[(i<<1)-1].id=i; bn[(i<<1)].u=q; bn[(i<<1)].v=p; bn[(i<<1)].id=i; } for(i=1;i<=n;i++) fl[i]=m*2+1; m*=2; sort(bn+1,bn+m+1); for(i=1;i<=m;i++) { fl[bn[i].u]=min(fl[bn[i].u],i); fr[bn[i].u]=max(fr[bn[i].u],i); } for(i=n-1;i>=1;i--) fl[i]=min(fl[i],fl[i+1]); for(i=1;i<n;i++) fr[i+1]=max(fr[i+1],fr[i]); for(i=1;i<=Q;i++) { scanf("%lld%lld",&que[i].l,&que[i].r); que[i].id=i; } sort(que+1,que+Q+1); for(i=0,k=1;i<=ks;i++) { for(j=1;j<=n;j++) fa[j]=j,size[j]=1; l=(i+1)*s,r=l; top=tail=0; for(;k<=Q&&fl[que[k].l]/s==i;k++) { p=fl[que[k].l],q=fr[que[k].r]; if(q/s==i) { an[que[k].id]=solve(p,q); continue; } for(;r<=q;r++) add(r); tail=top; for(j=l-1;j>=p;j--) add(j); an[que[k].id]=ans; for(j=l-1;j>=p;j--) cnt[bn[j].id]--; clear(); } for(j=r-1;j>=l;j--) cnt[bn[j].id]--; tail=0; clear(); } for(i=1;i<=Q;i++) printf("%lld\n",an[i]); } }