HNOI2016 最小公倍数

这是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 register
using 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值