BZOJ4537[HNOI2016]最小公倍数

来源:互联网 发布:百家讲坛知乎 编辑:程序博客网 时间:2024/06/10 09:34

那个,事情是这样的,从前有一只蒟蒻(对,就是我),然后有一天他心血来潮想创个博客。。

可是没过多久,他的热情就过去了,甚至连一篇博文都没有写。。

然后,不知道怎么了,他的博客就被机房里的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");}


原创粉丝点击