HNOI2016 最小公倍数

来源:互联网 发布:数据分析师月薪 编辑:程序博客网 时间:2024/06/06 06:38

这是HNOI2016的DAY1T1;

是一道众多CJ神犇口中的水题,也是YMD 用分块,莫队打天下的第一站;

这个题只要数学没有跪烂,应该还是可以看出来,

目标是要判定是否存在路径使x,y联通,且路上的a的最大值等于A,b的最大值等于B;

好我们先考虑暴力怎么解决;

对于前二十分,我们考虑对于每组询问,只加入a<=A且b<=B的边,在用并查集判断是否联通以及最大值是否为A和B;

好的,那么我们该怎么做呢;

首先我们身在HN,那么我们的脑海中一定要装着两个算法:莫队和CDQ,这是HN的骄傲!!

其次我们要谨记网管的教导“暴力出奇迹”,

“这题你不会,那你打个暴力就可以了嘛,暴力就可以AC嘛”;

谨记王队长的传奇“暴力进省队”;

所以这题的正解就是比纯暴力好一点点大暴力:分块!!!

思想和暴力及其类似:我们把边按照a sort,再进行分块,再把询问按照 b 进行排序;

1.我们对于每个块,先把前i-1个块中的边全部搞出来,再把对于满足这个块的a(即大于等于这个块的开头,小于等于这个块的结尾)的询问全部全部搞出来。并把搞出来的边按照b进行排序。

2.然后再枚举这些满足a的询问,有两步操作,一个是算这个块之前的贡献,一个是算这个块对当前询问的贡献;

3.第一步,因为前面已经按a排序了,所以前面的这些边的a值是一定满足询问的a的限制的,所以我们只要把已经按照b排了序的边判断是否满足询问的b的条件再依次加入

4.第二步,因为a和b都不一定满足要求,所以我们需要暴力对这个块中的边进行两次判断,即a和b都需要判断,再加入这条边;

5.并且由于满足这个块的询问的a并不一定是升序的,所以可能对于满足的两个询问i,j;

bi<bj,但ai>aj;这样就会有一个尴尬的问题,一条边的ax可能满足aj<ax<ai;显然这一条边在处理j的时候是不能算的,所以我们需要我们的并查集拥有回溯功能,即把刚刚加入的边删掉;

6.这样这个题目就可以AC了;

7.最后记得常数优化,cmp要打 const &,不然会gi烂;

// MADE BY QT666#include<cstdio>#include<algorithm>#include<cmath>#include<iostream>#include<cstdlib>#include<cstring>#include<string>#define RG registerusing namespace std;const int N=100050;int gi(){  RG int x=0;  char ch=getchar();  while(ch<'0'||ch>'9') ch=getchar();  while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();  return x;}int n,m,fa[N],maxa[N],maxb[N],size[N],tot2,tot,ans[N];int find(RG int x){return fa[x]==x?x:find(fa[x]);}struct ac{  int x,y,a,b,id;}e[N],query[N],old[N];struct AC{  int x,y,f,ma,mb,size;}add[N];bool cmpa(const ac &a,const ac &b){  if(a.a==b.a) return a.b<b.b;  return a.a<b.a;}bool cmpb(const ac &a,const ac &b){  if(a.b==b.b) return a.a<b.a;  return a.b<b.b;}void merge(int x,int y,int a,int b){  x=find(x),y=find(y);  if(size[x]>size[y]) swap(x,y);  add[++tot2]=(AC){x,y,fa[x],maxa[y],maxb[y],size[y]};  if(x==y) {maxa[y]=max(maxa[y],a),maxb[y]=max(maxb[y],b);return;}  fa[x]=y;size[y]+=size[x];  maxa[y]=max(maxa[x],max(maxa[y],a));  maxb[y]=max(maxb[x],max(maxb[y],b));}void del(){  for(RG int i=tot2;i;i--)    {      RG int x=add[i].x,y=add[i].y;      fa[x]=add[i].f,maxa[y]=add[i].ma,maxb[y]=add[i].mb,size[y]=add[i].size;    }  tot2=0;}int main(){  n=gi(),m=gi();  RG int block=(int)sqrt(m);  for(RG int i=1;i<=m;i++) e[i].x=gi(),e[i].y=gi(),e[i].a=gi(),e[i].b=gi(),e[i].id=i;  sort(e+1,e+1+m,cmpa);  RG int T=gi();  for(RG int i=1;i<=T;i++) query[i].x=gi(),query[i].y=gi(),query[i].a=gi(),query[i].b=gi(),query[i].id=i;  sort(query+1,query+1+T,cmpb);  for(RG int i=1;i<=m;i+=block)    {      tot=0;      for(RG int j=1;j<=T;j++){  if(query[j].a>=e[i].a&&(i+block>m||query[j].a<e[i+block].a))    {      old[++tot]=query[j];    }}      sort(e+1,e+1+i,cmpb);      for(RG int j=1;j<=n;j++) fa[j]=j,maxa[j]=maxb[j]=-1,size[j]=1;      RG int r=1;      for(RG int j=1;j<=tot;j++){  for(;r<i&&e[r].b<=old[j].b;r++)    {      merge(e[r].x,e[r].y,e[r].a,e[r].b);    }  tot2=0;  for(RG int l=i;l<i+block&&l<=m;l++)    if(e[l].a<=old[j].a&&e[l].b<=old[j].b)      {merge(e[l].x,e[l].y,e[l].a,e[l].b);      }  RG int x=find(old[j].x),y=find(old[j].y);  if(x==y&&maxa[x]==old[j].a&&maxb[x]==old[j].b) ans[old[j].id]=1;  else ans[old[j].id]=0;  del();        }    }  for(RG int i=1;i<=T;i++)    {      if(ans[i]==1) puts("Yes");      else puts("No");    }  return 0;}

0 0