https://atcoder.jp/contests/agc002/tasks/agc002_d
ABC傻逼题,感觉以前的AGC有点水,然而B题一开始想假了写了快20分钟
D题最后9分钟过了挺爽的
首先对于M条边,把整棵树连起来只有N-1条边,那么因为答案要求最小化最大边的序号
那么我们直接按序号连边,如果要合并,就新建一个点,记录下这个点对应的集合的大小时多少,然后连向合并的两个点,且边的权值为当前枚举的边的序号,相当于把完整的并查集树建出来,且不需要按秩合并
由于图是联通的,根节点一定是一个包含1-n的集合,然后预处理倍增数组,且记录向上跑路径的序号的最大值
因为每次连边,集合都会变大,所以从每个点开始倍增是跑得越上面,路径的边的最大值越大
那么我们对于每次询问,都二分一下答案,然后从x和y都想上跑,且最大边要小于二分的值,肯定是尽可能向上跑得更远因为越向上,边的路径越大,集合的大小越大
如果x,y跑到了同一个集合,那么就是这个集合x,y用小于等于二分的答案的边都能到达,就是这个集合大小,否则则各自集合相加
然而这样是Qlognlogn的,如果并查集树按秩合并,高度最高是logn,那么复杂度就是Qlogn*log(logn)(然而没必要)网上好像都是整体二分写的,而且据说可以优化到qlogn。。。之后去研究一下
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=3e5+10;
int n,m,q,cnt,tot,rt,cas,ans;
int a[maxl],f[maxl],t[maxl],sz[maxl],num[maxl];
int fa[21][maxl],mx[21][maxl];
bool vis[maxl];
char s[maxl];
struct ed
{
int v,l;
};
vector<ed> e[maxl];
inline int find(int x)
{
if(f[x]!=x)
f[x]=find(f[x]);
return f[x];
}
inline void dfs(int u)
{
for(ed ee:e[u])
{
fa[0][ee.v]=u;
mx[0][ee.v]=ee.l;
dfs(ee.v);
}
}
inline void prework()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
f[i]=i,t[i]=1,sz[i]=1,num[i]=i;
tot=n;
int u,v,x,y;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
x=find(u);y=find(v);
if(x!=y)
{
sz[++tot]=t[x]+t[y];a[tot-n]=i;
e[tot].push_back({num[x],i});
e[tot].push_back({num[y],i});
num[x]=tot;
f[y]=x;t[x]+=t[y];
++cnt;
if(cnt==n-1)
rt=tot;
}
}
dfs(rt);
for(int k=1;k<=20;k++)
for(int i=1;i<=tot;i++)
{
fa[k][i]=fa[k-1][fa[k-1][i]];
mx[k][i]=max(mx[k-1][i],mx[k-1][fa[k-1][i]]);
}
}
inline int up(int u,int l)
{
for(int i=20;i>=0;i--)
if(fa[i][u]!=0 && mx[i][u]<=l)
u=fa[i][u];
return u;
}
inline int calc(int x,int y,int mid)
{
int nx,ny;
nx=up(x,a[mid]);
ny=up(y,a[mid]);
return (nx==ny)?sz[nx]:sz[nx]+sz[ny];
}
inline void mainwork()
{
scanf("%d",&q);
int x,y,z;
for(int i=1;i<=q;i++)
{
scanf("%d%d%d",&x,&y,&z);
int l=0,r=n-1,mid;
while(l+1<r)
{
mid=(l+r)>>1;
if(calc(x,y,mid)>=z)
r=mid;
else
l=mid;
}
if(calc(x,y,l)>=z)
ans=a[l];
else
ans=a[l+1];
printf("%d\n",ans);
}
}
inline void print()
{
}
int main()
{
int t=1;
//scanf("%d",&t);
for(cas=1;cas<=t;cas++)
{
prework();
mainwork();
print();
}
return 0;
}


被折叠的 条评论
为什么被折叠?



