HNOI2016 最小公倍数 题解
很神秘的分块优化暴力。
首先有一个暴力的想法,每次取出所有 \(a\le A,b\le B\) 的边,实际上就是判断 \(u,v\) 是否连通且连通块内是否有 \(a_{max}=A,b_{max}=B\)。这样是 \(O(mq)\) 的。
考虑对所有边按 \(a\) 排序,然后每 \(B\) 个分块。
我们考虑处理 \(A\) 在当前块 \(a\) 范围内的询问,那么前面的块的 \(a\) 一定满足限制。
考虑怎样满足 \(B\) 的限制,我们可以对前面块的所有边按 \(b\) 排序,然后双指针用并查集加边。
而对于当前块内同时满足 \(A,B\) 限制的边,这些边集并不是互相包含的,所以我们要独立贡献它们,用所有满足的边在缩完的并查集上跑搜索即可。
那么排序部分是 \(O(\frac {m^2}B\log m)\),对前面的块加边是 \(O(\frac {m^2}B\alpha(n))\),而对于块内的搜索是 \(O(qB\alpha(n))\) 的。
总复杂度应为 \(O(\frac {m^2}B\log m+qB)\)。取 \(B=O(\frac m{\sqrt q})\) 时,复杂度达到 \(O(m\sqrt q\log m)\)。
细节
-
对于连向同一连通块内的边,记得更新 \(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;
}