BZOJ4537 : [Hnoi2016]最小公倍数

将边按$a$从小到大排序,每$\sqrt{m}$个取一个关键点。

对于每个关键点,将这个点之前的边以及要在这个关键点回答的询问按$b$排序。

依次加入这个关键点之前的每条边,用并查集维护每个连通块$a$和$b$的最大值。

对于零碎部分,只有$\sqrt{m}$条边,暴力加入即可。

用一个栈按时间记录所有修改操作,然后撤销操作即可。

时间复杂度$O(m\sqrt{m}\log n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=50010,M=100010;
int n,m,Q,lim,i,j,k,o,x,y,ans[N],cnt,a[M],b[N];
int f[N],d[N],va[N],vb[N],co;
struct P{int x,y,z;P(){}P(int _x,int _y,int _z){x=_x,y=_y;z=_z;}}op[N];
struct E{int x,y,a,b;}e[M],q[N];
inline bool cmpa(const E&a,const E&b){return a.a<b.a;}
inline bool cmpe(int x,int y){return e[x].b<e[y].b;}
inline bool cmpq(int x,int y){return q[x].b<q[y].b;}
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
int F(int x){return f[x]==x?x:F(f[x]);}
inline void merge(int x,int y,int a,int b,int type){
  x=F(x),y=F(y);
  if(x!=y){
    if(d[x]==d[y]){
      if(type)op[++co]=P(1,x,d[x]);
      d[x]++;
    }
    if(d[x]<d[y])swap(x,y);
    if(type)op[++co]=P(0,y,y);
    f[y]=x;
    if(va[x]<va[y]&&va[y]>a){
      if(type)op[++co]=P(2,x,va[x]);
      va[x]=va[y];
    }
    if(vb[x]<vb[y]&&vb[y]>b){
      if(type)op[++co]=P(3,x,vb[x]);
      vb[x]=vb[y];
    }
  }
  if(va[x]<a){
    if(type)op[++co]=P(2,x,va[x]);
    va[x]=a;
  }
  if(vb[x]<b){
    if(type)op[++co]=P(3,x,vb[x]);
    vb[x]=b;
  }
}
inline void retrace(){
  for(int i=co;i;i--){
    if(!op[i].x)f[op[i].y]=op[i].z;
    else if(op[i].x==1)d[op[i].y]=op[i].z;
    else if(op[i].x==2)va[op[i].y]=op[i].z;
    else vb[op[i].y]=op[i].z;
  }
  co=0;
}
int main(){
  read(n),read(m);
  while(lim*lim<m)lim++;
  for(i=1;i<=m;i++)read(e[i].x),read(e[i].y),read(e[i].a),read(e[i].b),a[i]=i;
  sort(e+1,e+m+1,cmpa);
  read(Q);
  for(i=1;i<=Q;i++)read(q[i].x),read(q[i].y),read(q[i].a),read(q[i].b);
  for(i=0;i<=m;i+=lim){
    cnt=0;
    for(j=1;j<=Q;j++)if(q[j].a>=e[i].a&&(i+lim>m||q[j].a<e[i+lim].a))b[++cnt]=j;
    if(!cnt)continue;
    sort(a+1,a+i+1,cmpe);
    sort(b+1,b+cnt+1,cmpq);
    for(j=1;j<=n;j++)f[j]=j,d[j]=0,va[j]=vb[j]=-1;
    for(j=k=1;j<=cnt;j++){
      while(k<=i&&e[a[k]].b<=q[b[j]].b){
        merge(e[a[k]].x,e[a[k]].y,e[a[k]].a,e[a[k]].b,0);
        k++;
      }
      co=0;
      for(o=min(i+lim-1,m);o>i;o--)
        if(e[o].a<=q[b[j]].a&&e[o].b<=q[b[j]].b)
          merge(e[o].x,e[o].y,e[o].a,e[o].b,1);
      x=F(q[b[j]].x),y=F(q[b[j]].y);
      if(x==y&&va[x]==q[b[j]].a&&vb[x]==q[b[j]].b)ans[b[j]]=1;
      retrace();
    }
  }
  for(i=1;i<=Q;i++)puts(ans[i]?"Yes":"No");
  return 0;
}

  

时间: 2024-08-12 10:53:35

BZOJ4537 : [Hnoi2016]最小公倍数的相关文章

[BZOJ4537][HNOI2016]最小公倍数(分块+并查集)

4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1687  Solved: 607[Submit][Status][Discuss] Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值.所有权值都可以分解成2^a*3^b 的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得 路径依次经过的边上的权值的最

【BZOJ4537】[Hnoi2016]最小公倍数 分块

[BZOJ4537][Hnoi2016]最小公倍数 Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值.所有权值都可以分解成2^a*3^b的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公倍数为2^a*3^b.注意:路径可以不是简单路径.下面是一些可能有用的定义:最小公倍数:K个数a1,a2,…,ak的最小公倍数是能被每个ai整除的最小正整数.路径:路径P:P

BZOJ 4537: [Hnoi2016]最小公倍数

4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 400[Submit][Status][Discuss] Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值.所有权值都可以分解成2^a*3^b的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公

4537: [Hnoi2016]最小公倍数

Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值.所有权值都可以分解成2^a*3^b的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公倍数为2^a*3^b.注意:路径可以不是简单路径.下面是一些可能有用的定义:最小公倍数:K个数a1,a2,…,ak的最小公倍数是能被每个ai整除的最小正整数.路径:路径P:P1,P2,…,Pk是顶点序列,满足对于任意1<=i<

2017.7.22 hnoi2016 最小公倍数

#include<cmath> #include<cstdio> #include<iostream> #include<algorithm> #define N 50010 #define M 100010 #define RG register #define inf 0x3f3f3f3f using namespace std; bool ans[N]; struct stac{ int u,v,op; }sta[40000]; struct node

4537: [Hnoi2016]最小公倍数|分块

暴力的做法就是直接找到所有a,b都小于等于某个询问的边然后并查集合并,维护每个集合的a,b得最大值看是否等于询问的a,b 然后就可以考虑分块,把边按照a排序,每隔n?√分为一块 块前的按照b值排序按顺序插入,块内的暴力判断,并查集合并,每次都把块内合并的记录下来,处理完某个询问时就撤回并查集的操作 块的大小为n?√可能会T 改成n?log2n????????√可能会快一点 #include<algorithm> #include<iostream> #include<cstd

bzoj4537【HNOI2016】最小公倍数

4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec  Memory Limit: 512 MB Submit: 563  Solved: 236 [Submit][Status][Discuss] Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,-,n),每条边上带有权值.所有权值都可以分解成2^a*3^b 的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得 路径依次经过的边上的权值的

YCB 的暑期计划

前言 YCB现在很弱(TAT) 暑假有一个月,赶快狂补一下. 大概的计划如下: 首先前期会以数据结构为主,毕竟代码能力太弱,涉及内容:线段树分治.二进制分组.KD-Tree. 等数据结构做到没有智商的时候加入一波数论,内容为 杜教筛.min_25筛. 然后中途小清新一下,做一些 组合博弈与构造题. 接着继续练代码能力,顺便学一些神奇的暴力:启发式合并.dsu on tree . 然后图论也忘的差不多了,就回过头去学点新东西,大概会有spfa判负环.0/1分数规划.差分约束. 估计这个时候也没有什

HDU 1108: 最小公倍数

最小公倍数 #include<iostream> using namespace std; int gcd(int a, int b) { while (b) { int t = a%b; a = b; b = t; } return a; } int lcm(int a, int b) { return a / gcd(a, b)*b; } int main() { ios::sync_with_stdio(false); int a, b; while (cin >> a &g