HNOI2016 最小公倍数 题解

HNOI2016 最小公倍数 题解

很神秘的分块优化暴力。

首先有一个暴力的想法,每次取出所有 aA,bB 的边,实际上就是判断 u,v 是否连通且连通块内是否有 amax=A,bmax=B。这样是 O(mq) 的。

考虑对所有边按 a 排序,然后每 B 个分块。

我们考虑处理 A 在当前块 a 范围内的询问,那么前面的块的 a 一定满足限制。

考虑怎样满足 B 的限制,我们可以对前面块的所有边按 b 排序,然后双指针用并查集加边。

而对于当前块内同时满足 A,B 限制的边,这些边集并不是互相包含的,所以我们要独立贡献它们,用所有满足的边在缩完的并查集上跑搜索即可。

那么排序部分是 O(m2Blogm),对前面的块加边是 O(m2Bα(n)),而对于块内的搜索是 O(qBα(n)) 的。

总复杂度应为 O(m2Blogm+qB)。取 B=O(mq) 时,复杂度达到 O(mqlogm)

细节

  • 对于连向同一连通块内的边,记得更新 maxa,maxb

  • 记得询问和边都要排序。

  • 由于有询问的 a,b=0 的情况,所以 maxa,maxb 的初值需要设为 1

  • 如果是用指针扫询问,记得要把询问的 a 小于边权的 min{a} 的询问过滤掉。

AC 代码:

const int N=1e5+5;
const int B=448;
int n,m;
struct arr{
int u,v,a,b,num;
void Read(){
return read(u,v,a,b);
}
};
int fa[N],sz[N];
int getfa(int x){
if(fa[x]==x)return x;
return fa[x]=getfa(fa[x]);
}
int maxa[N],maxb[N];
void merge(int x,int y,int a,int b){
x=getfa(x),y=getfa(y);
if(x==y){
maxa[x]=max(maxa[x],a);
maxb[x]=max(maxb[x],b);
return ;
}
if(sz[x]<sz[y])swap(x,y);
maxa[x]=max(maxa[x],maxa[y]);
maxb[x]=max(maxb[x],maxb[y]);
maxa[x]=max(maxa[x],a);
maxb[x]=max(maxb[x],b);
sz[x]+=sz[y];
fa[y]=x;
}
arr e[N],q[N];
int Q;
int ans[N];
int vis[N],bid;
struct edge{
int v,a,b;
};
vector<edge> g[N];
signed main(){
read(n,m);
fo(i,1,m){
e[i].Read();
}
sort(e+1,e+m+1,[](arr a,arr b){
return a.a<b.a;
});
read(Q);
fo(i,1,Q){
q[i].Read();
q[i].num=i;
}
sort(q+1,q+1+Q,[](arr a,arr b){
return a.a<b.a;
});
int L=1;
e[m+1].a=1e9+1;
while(L<=Q&&q[L].a<e[1].a)++L;
for(int l=1,r;l<=m;l=r+1){
r=min(m,l+B-1);
sort(e+1,e+l,[](arr a,arr b){
return a.b<b.b;
});
int R=L-1;
while(R<Q&&q[R+1].a>=e[l].a&&q[R+1].a<=e[r].a&&q[R+1].a<e[r+1].a)++R;
fo(i,1,n)fa[i]=i,sz[i]=1,maxa[i]=maxb[i]=-1;
int t=1;
sort(q+L,q+R+1,[](arr a,arr b){
return a.b<b.b;
});
fo(i,L,R){
while(t<l&&e[t].b<=q[i].b){
merge(e[t].u,e[t].v,e[t].a,e[t].b);
++t;
}
vector<int> vec;
fo(j,l,r){
if(e[j].a<=q[i].a&&e[j].b<=q[i].b){
int a=getfa(e[j].u),b=getfa(e[j].v);
g[a].push_back({b,e[j].a,e[j].b}),g[b].push_back({a,e[j].a,e[j].b}),vec.push_back(a),vec.push_back(b);
}
}
++bid;
queue<int> p;
p.push(getfa(q[i].u)),vis[p.front()]=bid;
int ma=-1,mb=-1,bz=0;
while(p.size()){
int u=p.front();
p.pop();
if(u==getfa(q[i].v))bz=1;
ma=max(ma,maxa[u]),mb=max(mb,maxb[u]);
for(auto v:g[u]){
if(vis[v.v]!=bid){
vis[v.v]=bid;
p.push(v.v);
}
ma=max(ma,v.a),mb=max(mb,v.b);
}
}
if(bz&&ma==q[i].a&&mb==q[i].b)ans[q[i].num]=1;
for(auto x:vec)g[x].clear();
}
L=R+1;
}
fo(i,1,Q)if(ans[i])write("Yes\n");
else write("No\n");
return 0;
}
posted @   dengchengyu  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示