bzoj4537[HNOI2016]最小公倍数
如果信息只有一维,就可以直接排序,扫一遍的过程中用并查集来维护,也就是u和v需要连通且连通块内的最大边权等于询问的边权。现在有两维,我一开始还往分治的方向去想,但是询问所需要的在每一层中的信息不好合并,于是就考虑分块。
首先把边按a排序,分块,把询问挂在第一个a大于询问a的那条边所在的块上,然后每一块内的边,每一块上的询问按b排序。
从左往右扫每个块,并把这个块左边的所有边都加入并查集中,且这个块之前的边按b归并排序好,对于一个块中的询问,记一个指针表示这个块之前的那些块中最多可以加到哪条边(按b比较),然后暴力扫当前块中符合它要求的边(\(a\le A_q且 b\le B_q\))加入并查集中,得到答案后就把当前块的这几条边撤回。每做完一个块就把它和之前的块按b归并排序。
可能讲的不大清楚,懂了大致意思后可以自己继续想一想细节问题。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<ctime>
#define P puts("lala")
#define cp cerr<<"lala"<<endl
#define fi first
#define se second
#define ln putchar('\n')
#define pb push_back
#define shmem(x) cerr<<sizeof(x)/(1024*1024.0)<<"MB"<<endl
using namespace std;
inline int read()
{
char ch=getchar();int g=1,re=0;
while(ch<'0'||ch>'9'){if(ch=='-')g=-1; ch=getchar();}
while(ch<='9'&&ch>='0') re=(re<<1)+(re<<3)+(ch^48),ch=getchar();
return re*g;
}
typedef long long ll;
typedef pair<int,int> pii;
const int N=100050;
struct node
{
int x,y,a,b,id;
node(int x=0,int y=0,int a=0,int b=0,int id=0):x(x),y(y),a(a),b(b),id(id) { }
};
inline bool operator < (node a,node b) {return a.b<b.b;}
inline bool cmp1(node a,node b) {return a.a<b.a;}
inline bool cmp2(node a,node b) {return a.b<b.b;}
node e[N],q[N];
int n,m,blosiz,bel[N],Ans[N];
vector<node>ve[500];
int fa[N];
inline int find(int x)
{
while(fa[x]!=x) x=fa[x];
return x;
}
node stk[N]; int top=0;
int maxa[N],maxb[N],siz[N];
inline void merge(int x,int y,int a,int b)
{
x=find(x); y=find(y);
if(siz[x]>siz[y]) swap(x,y);
stk[++top]=node(x,y,maxa[y],maxb[y],siz[y]);
maxa[y]=max(maxa[y],max(maxa[x],a)); maxb[y]=max(maxb[y],max(maxb[x],b));
if(x==y) return ;
siz[y]+=siz[x]; fa[x]=y;
}
inline void undo()
{
int x=stk[top].x,y=stk[top].y;
siz[y]=stk[top].id; maxa[y]=stk[top].a; maxb[y]=stk[top].b;
fa[x]=x;
top--;
}
node now[N],tmp[N]; int tot=0;
void mergesort(int l,int r)
{
int p1=1,p2=l,p3=1;
while(p1<=tot&&p2<=r)
{
if(now[p1]<e[p2]) tmp[p3]=now[p1],p1++,p3++;
else tmp[p3]=e[p2],p2++,p3++;
}
while(p1<=tot) tmp[p3]=now[p1],p1++,p3++;
while(p2<=r) tmp[p3]=e[p2],p2++,p3++;
tot+=r-l+1;
for(int i=1;i<=tot;++i) now[i]=tmp[i];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);freopen("1.out","w",stdout);
#endif
n=read(); m=read();
for(int i=1;i<=m;++i)
e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read();
int Q=read();
for(int i=1;i<=Q;++i)
q[i].x=read(),q[i].y=read(),q[i].a=read(),q[i].b=read(),q[i].id=i;
sort(e+1,e+1+m,cmp1);
blosiz=sqrt(m);
for(int i=1;i<=m;++i) bel[i]=(i-1)/blosiz+1;
sort(q+1,q+1+Q,cmp1);
for(int i=1,p1=1;i<=m;++i)
{
while(p1<=Q&&(q[p1].a<e[i].a||i==m)) ve[bel[i]].pb(q[p1]),p1++;
}
for(int i=1;i<=bel[m];++i) sort(ve[i].begin(),ve[i].end());
for(int i=1;i<=bel[m];++i) sort(e+(i-1)*blosiz+1,e+min(i*blosiz,m)+1);
for(int o=1;o<=bel[m];++o)
{
top=0;
for(int i=1;i<=n;++i) fa[i]=i,maxa[i]=maxb[i]=-1,siz[i]=1;
int siz=ve[o].size(),p1=1;
for(int w=0;w<siz;++w)
{
while(p1<=tot&&ve[o][w].b>=now[p1].b) //in front of the block
merge(now[p1].x,now[p1].y,now[p1].a,now[p1].b),p1++;
int last=top;
for(int i=(o-1)*blosiz+1;i<=m&&i<=o*blosiz;++i) //in the block
if(e[i].b<=ve[o][w].b)
{
if(e[i].a<=ve[o][w].a)
merge(e[i].x,e[i].y,e[i].a,e[i].b);
}
else break;
int r1=find(ve[o][w].x),r2=find(ve[o][w].y);
if(r1==r2&&maxa[r1]==ve[o][w].a&&maxb[r1]==ve[o][w].b)
Ans[ve[o][w].id]=1;
while(top!=last) undo();
}
mergesort((o-1)*blosiz+1,min(o*blosiz,m));
}
for(int i=1;i<=Q;++i) puts(Ans[i]==1?"Yes":"No");
return 0;
}