[DS记录]AT1998 [AGC002D] Stamp Rally

command_block

2021-02-28 21:18:25

Personal

**题意** : 给出一张 $n$ 个点 $m$ 条边的无向连通图。 $q$ 次询问从两个点 $x,y$ 出发,希望经过的点(不重复)数量等于 $z$ ,经过的边最大编号最小是多少。 $n,m,q\leq 10^5$ ,时限$\texttt{2s}$。 ------------ 考虑整体二分。BFS + 并查集即可做到 $O\big(n\alpha(n)\log n\big)$。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 100500 using namespace std; int f[MaxN],c[MaxN]; void Init(int n) {for (int i=1;i<=n;i++)c[f[i]=i]=1;} int find(int u) {return f[u]==u ? u : f[u]=find(f[u]);} void merge(int u,int v){ u=find(u);v=find(v); if (u==v)return ; if (c[u]>c[v])swap(u,v); c[f[u]=v]+=c[u]; } struct Line{int u,v;}l[MaxN]; struct QData{int u,v,c,ans;}b[MaxN]; struct MData{int l,r,tl,tr;}t[MaxN<<1]; int tim,ql,qr; void ltg(int lim){ for (;tim<=lim;++tim) merge(l[tim].u,l[tim].v); } int calc(int u,int v){ u=find(u);v=find(v); return u==v ? c[u] : c[u]+c[v]; } int p[MaxN],sp[MaxN]; void solve(int l,int r,int tl,int tr) { if (tl==tr){ for (int i=l;i<=r;i++)b[p[i]].ans=tl; return ; }int tmid=(tl+tr)>>1,mid=l-1,tn=0; ltg(tmid); for (int k=l;k<=r;k++){ int i=p[k]; if (calc(b[i].u,b[i].v)>=b[i].c)p[++mid]=i; else sp[++tn]=i; } for (int i=1;i<=tn;i++)p[i+mid]=sp[i]; if (l<=mid)t[++qr]=(MData){l,mid,tl,tmid}; if (mid<r)t[++qr]=(MData){mid+1,r,tmid+1,tr}; } int n,m,q; int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) scanf("%d%d",&l[i].u,&l[i].v); scanf("%d",&q); for (int i=1;i<=q;i++) scanf("%d%d%d",&b[p[i]=i].u,&b[i].v,&b[i].c); t[ql=qr=1]=(MData){1,q,1,m}; while(ql<=qr){ int sav=qr; Init(n);tim=1; for (int i=ql;i<=sav;i++) solve(t[i].l,t[i].r,t[i].tl,t[i].tr); ql=sav+1; } for (int i=1;i<=q;i++) printf("%d\n",b[i].ans); return 0; } ```