那个,事情是这样的,从前有一只蒟蒻(对,就是我),然后有一天他心血来潮想创个博客。。
可是没过多久,他的热情就过去了,甚至连一篇博文都没有写。。
然后,不知道怎么了,他的博客就被机房里的dalao翻出来了,还被嘲讽了一番。。
于是他决定补救一下他的博客。。嗯,就这样。。
这道题大概是这样的。。给定一张n个点m条边的带权无向图,图上的边权都是的格式
然后给定q个询问,询问有四个参数u,v,a,b,问是否存在一条从u到v的路径使得路径上所有边权的最小公倍数为(不一定要是简单路径)
首先,转化一下题目。。最小公倍数的部分相当于路径上最大的a值和b值等于询问的a值和b值
然后又不一定需要是简单路径,所以所求路径等价于包含u和v的一个连通块
对于前20%的数据,暴力算法十分的明显,对于每个询问i,维护一个并查集,只加入且的边
最后只要满足f[u]==f[v]且并查集内最大的a值和b值等于ai和bi则符合题意,然而这个算法只能获得20%的分数
考虑用分块优化这个暴力,将所有的边按a排序
对于第i个块,将a值在块的值的范围内的询问取出来
将前i-1个块的边和取出来的询问按b排序,由于所有的a满足要求,b的值为递增的,将边按顺序加入并查集,判断一下即可。
考虑对答案有贡献的边有可能在第i个块内,且询问的a并不是递增的,所以需要对于每个询问,将第i个块内的边依次加入,判断后删除即可,这样的边最多条。。需要并查集支持回溯功能,这个只需按秩合并然后开个栈存一下加边前的状态就好了
时间复杂度为,由于是第一次做分块的题,没什么就经验,直接用sqrt(m)做块的大小。。T了好几次,最后参考了神犇的程序,把块的大小换成常数就A了QAQ
#include<cmath>
#include<cstdio>
#include<algorithm>
using std::sort;
const int N=50005;
inline int read()
{
int t=0,c=getchar();
for(;c<48||c>57;c=getchar());
do
{
t=(t<<1)+(t<<3)+c-48;
c=getchar();
}
while(47<c&&c<58);
return t;
}
struct data
{
int u,v,a,b,id;
void init(int i)
{
id=i;u=read();v=read();
a=read();b=read();
}
}q[N],e[N<<1],st[N];
struct lx{int u,v,a,b,s;}z[N<<1];
int n,m,Q,S,tot,top,u,v,f[N],a[N],b[N],s[N];bool ans[N];
inline bool cmp(data a,data b)
{
if(a.a==b.a)return a.b<b.b;
return a.a<b.a;
}
inline bool cmp2(data a,data b)
{
if(a.b==b.b)return a.a<b.a;
return a.b<b.b;
}
inline int max(int a,int b){a-=b;return b+(a&(~a>>31));}
inline void swap(int &a,int &b){a^=b;b^=a;a^=b;}
inline int get(int x){return f[x]==x?x:get(f[x]);}
inline void merge(int u,int v,int A,int B)
{
u=get(u);v=get(v);
if(s[v]<s[u])swap(u,v);
z[++tot]=(lx){u,v,a[v],b[v],s[v]};
if(u==v)
{
a[u]=max(a[u],A);
b[u]=max(b[u],B);
return;
}
f[u]=v;s[v]+=s[u];
a[v]=max(a[v],max(a[u],A));
b[v]=max(b[v],max(b[u],B));
}
inline void back()
{
int x,y;
for(;tot;--tot)
{
f[x=z[tot].u]=x;
a[y=z[tot].v]=z[tot].a;
b[y]=z[tot].b;
s[y]=z[tot].s;
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;++i)e[i].init(i);
sort(e+1,e+m+1,cmp);scanf("%d",&Q);
for(int i=1;i<=Q;++i)q[i].init(i);
sort(q+1,q+Q+1,cmp2);S=755;
for(int i=1,x;i<=m;i+=S)
{
top=0;x=i+S;
for(int j=1;j<=Q;++j)
if(q[j].a>=e[i].a&&(x>m||q[j].a<e[x].a))
st[++top]=q[j];
sort(e+1,e+i,cmp2);
for(int j=1;j<=n;++j)f[j]=j,s[j]=1,a[j]=b[j]=-1;
for(int j=1,k=1;j<=top;++j)
{
for(;k<i&&e[k].b<=st[j].b;++k)
merge(e[k].u,e[k].v,e[k].a,e[k].b);
tot=0;
for(int l=i;l<x&&l<=m;++l)
if(e[l].a<=st[j].a&&e[l].b<=st[j].b)
merge(e[l].u,e[l].v,e[l].a,e[l].b);
u=get(st[j].u);v=get(st[j].v);
ans[st[j].id]=(u==v&&a[u]==st[j].a&&b[u]==st[j].b);
back();
}
}
for(int i=1;i<=Q;++i)
if(ans[i])printf("Yes\n");
else printf("No\n");
}