【agc002d】Stamp Rally
阿新 • • 發佈:2018-05-28
二分答案 set scan algo sign del mes AR printf
題目大意
無向圖中,每次詢問從x和y分別出發,一共經過z個點,使需要走過編號最大的邊最小。
解題思路
對於暴力,我們對於每個詢問二分答案ans,將1~ans的邊加入,用並查集維護,如果x和y在同一個並查集,則判斷該並查集大小是否大於等於z,否則判斷該x所在並查集和y所在並查集大小的和是否大於等於z。
考慮如何優化,整體二分,二分邊的編號,同時對於每個編號區間[l,r],記錄一個詢問集合,表示該集合的詢問的答案在區間[l,r]中。同時用可撤銷並查集維護。
#include <cmath> #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <queue> #include <map> #include <bitset> #include <set> const int maxlongint=2147483647; const int mo=1e9+7; const int N=100005; using namespace std; struct arr { int x,y,z,p; }ask[N],out[N]; int n,m,q,a[N][2],ans[N],fa[N],val[N],t[N],size[N],tv[N]; int get(int x) { return x==fa[x]?x:get(fa[x]); } void mesh(int x,int y) { int xx=get(x),yy=get(y); if(xx==yy) return; if(val[xx]>val[yy]) fa[yy]=xx,t[++t[0]]=yy,size[xx]+=size[yy],tv[t[0]]=val[xx]; else { fa[xx]=yy,t[++t[0]]=xx,size[yy]+=size[xx]; tv[t[0]]=val[yy]; if(val[xx]==val[yy]) val[yy]++; } } void del() { int x=t[t[0]]; size[fa[x]]-=size[x],val[fa[x]]=tv[t[0]]; fa[x]=x; t[0]--; } void dc(int l,int r,int L,int R) { if(L>R) return; if(l==r) { for(int i=L;i<=R;i++) ans[ask[i].p]=l; return; } int mid=(l+r)>>1,mm=L-1,sign=t[0]; for(int i=l;i<=mid;i++) mesh(a[i][0],a[i][1]); for(int i=L;i<=R;i++) { int xx=get(ask[i].x),yy=get(ask[i].y),vv=(xx==yy?size[xx]:size[xx]+size[yy]); if(vv>=ask[i].z) swap(ask[i],ask[++mm]); } dc(mid+1,r,mm+1,R); for(;t[0]>sign;del()); dc(l,mid,L,mm); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d",&a[i][0],&a[i][1]); scanf("%d",&q); for(int i=1;i<=q;i++) scanf("%d%d%d",&ask[i].x,&ask[i].y,&ask[i].z),ask[i].p=i; for(int i=1;i<=n;i++) fa[i]=i,size[i]=1; dc(1,m,1,q); for(int i=1;i<=q;i++) printf("%d\n",ans[i]); }
【agc002d】Stamp Rally