zoukankan      html  css  js  c++  java
  • bzoj 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<k,节点Pi和Pi+1之间都有边相连。简单路径:如果路径P:P1,P2,…,Pk中,对于任意1
    <=s≠t<=k都有Ps≠Pt,那么称路径为简单路径。

    Input

      输入文件的第一行包含两个整数N和M,分别代表图的顶点数和边数。接下来M行,每行包含四个整数u、v、a、
    b代表一条顶点u和v之间、权值为2^a*3^b的边。接下来一行包含一个整数q,代表询问数。接下来q行,每行包含四
    个整数u、v、a和b,代表一次询问。询问内容请参见问题描述。1<=n,q<=50000、1<=m<=100000、0<=a,b<=10^9

    Output

      对于每次询问,如果存在满足条件的路径,则输出一行Yes,否则输出一行 No(注意:第一个字母大写,其余
    字母小写) 。

    Sample Input

    4 5
    1 2 1 3
    1 3 1 2
    1 4 2 1
    2 4 3 2
    3 4 2 2
    5
    1 4 3 3
    4 2 2 3
    1 3 2 2
    2 3 2 2
    1 3 4 4

    Sample Output

    Yes
    Yes
    Yes
    No
    No
     
    这题就算数学只有幼儿园水平了应该也知道要求什么吧。。。
    就是给定一个图,每条边有两个权值:a和b,有多组询问,让你判断是否在两点间存在一条路径使路径上a的最大值为A,b的最大值为B;
    首先考虑暴力的做法:对于每组询问我们只加入满足A限制的边,再判断两点是否连通以及判断最大值是否满足。
    这个东西的维护可以用一个叫带权并查集的一个东西,听起来很高端其实就是在并查集合并时加了一个数组而已
    附上带权并查集代码:
    1 void merge(int x,int y,int a,int b)
    2 {
    3     int X=find(x),Y=find(y);
    4     if(size[X]>size[Y]) swap(X,Y);
    5     if(X==Y){maxa[Y]=max(maxa[Y],a),maxb[Y]=max(maxb[Y],b);return;}
    6     fa[X]=Y;size[Y]+=size[X];
    7     maxa[Y]=max(maxa[Y],max(a,maxa[X]));
    8     maxb[Y]=max(maxb[Y],max(b,maxb[X]));
    9 }

    怎么暴力怎么来。。。

    考虑到这种涉及两个权值的问题一般都要限制住一个条件

    这题用在线算法是做不了的,貌似在线的话就只能对每个询问暴力搞了吧。。。

    因为这种做法的缺陷在于它每次都要对所有的边进行处理,即必须对每个询问都重新构图,暴力判断。。。

    那么我们考虑离线做法吧。。。

    这题的思想极其巧妙,把边按照a的权值分块,询问按照b的权值排序!!!

    -----真的不知道怎么想出来的。

    直接说做法吧。。。

    1.对于每个块,把满足这个块的A的条件的询问找出来。。。

    2.我们对于这些满足的询问分两种情况来考虑目前对该询问的贡献:这个块之前的边(整块),这个块目前的边(非整块);

    3.对于第一种情况,那么对于这些询问来说,这个块以前的块中的边是一定A的条件的!!!(因为是按照a从小到大排序了的)

    4.也就是说这些这些边只要满足b的条件即可,那么可以把这些边按bsort。

    5.再把这些边只需按照满足b的条件依次加入即可。。。注意这些加入的边对于以后的询问也是会用到的,因为询问的边的b是递增的,所以每次不需重构图。

    6.对于第二种情况,这些边的a和b都需要满足条件。。

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

      bi<bj,但ai>aj;这样就会有一个尴尬的问题,一条边的ax可能满足aj<ax<ai;显然这一条边在处理j的时候是不能算的,所以我们需要我们的并查集拥有回溯功能,即把刚刚加入  的边删掉;利用栈把每次加边之前的状态全部记录下来即可,加完后在回溯。

    8.第一遍WA了,有点醉。。。

    1 for(int i=1;i<=q;i++)
    2     {
    3       if(ans[i]) puts("YES");
    4       else puts("NO");
    5     }

    具体实现如下:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<cmath>
     6 using namespace std;
     7 const int N=1000050;
     8 int gi()
     9 {
    10     int x=0;
    11     char ch=getchar();
    12     while(ch<'0'||ch>'9') ch=getchar();
    13     while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    14     return x;
    15 }
    16 struct ac
    17 {
    18     int x,y,a,b,id;
    19 }edge[N],query[N];
    20 int n,m,q,block,pos[N],l[N],r[N],cnt,canuse[N],tt;
    21 int fa[N],maxa[N],maxb[N],size[N],ans[N];
    22 struct AC
    23 {
    24     int x,y,f,ma,mb,size;
    25 }add[N];
    26 bool cmpa(const ac &a,const ac &b)
    27 {
    28   if(a.a==b.a) return a.b<b.b;
    29   return a.a<b.a;
    30 }
    31 bool cmpb(const ac &a,const ac &b)
    32 {
    33   if(a.b==b.b) return a.a<b.a;
    34   return a.b<b.b;
    35 }
    36 int find(int x){return fa[x]==x?x:find(fa[x]);}
    37 void merge(int x,int y,int a,int b)
    38 {
    39     int X=find(x),Y=find(y);
    40     if(size[X]>size[Y]) swap(X,Y);
    41     add[++tt]=(AC){X,Y,fa[X],maxa[Y],maxb[Y],size[Y]};
    42     if(X==Y){maxa[Y]=max(maxa[Y],a),maxb[Y]=max(maxb[Y],b);return;}
    43     fa[X]=Y;size[Y]+=size[X];
    44     maxa[Y]=max(maxa[Y],max(a,maxa[X]));
    45     maxb[Y]=max(maxb[Y],max(b,maxb[X]));
    46 }
    47 void del()
    48 {
    49     for(int i=tt;i>=1;i--) 
    50     {
    51         int x=add[i].x,y=add[i].y;
    52         fa[x]=add[i].f;maxa[y]=add[i].ma;maxb[y]=add[i].mb;size[y]=add[i].size;
    53     }
    54     tt=0;
    55 }
    56 int main()
    57 {
    58     n=gi(),m=gi();
    59     for(int i=1;i<=m;i++) edge[i].x=gi(),edge[i].y=gi(),edge[i].a=gi(),edge[i].b=gi();
    60     q=gi();
    61     for(int i=1;i<=q;i++) query[i].x=gi(),query[i].y=gi(),query[i].a=gi(),query[i].b=gi(),query[i].id=i;
    62     sort(edge+1,edge+1+m,cmpa);
    63     block=(int)sqrt(m);
    64     sort(query+1,query+1+q,cmpb);
    65     for(int i=1;i<=m;i+=block)
    66     {
    67       int tot=0;
    68       for(int j=1;j<=q;j++) 
    69           if(query[j].a>=edge[i].a&&(query[j].a<edge[i+block].a||i+block>m))
    70           canuse[++tot]=j;      
    71       sort(edge+1,edge+i+1,cmpb);
    72       for(int j=1;j<=n;j++) fa[j]=j,size[j]=1,maxa[j]=-1,maxb[j]=-1;
    73       int r=1;
    74       for(int j=1;j<=tot;j++)
    75       {
    76          for(;r<i&&query[canuse[j]].b>=edge[r].b;r++) merge(edge[r].x,edge[r].y,edge[r].a,edge[r].b);
    77          tt=0;
    78          for(int p=i;p<i+block&&p<=m;p++)
    79          {
    80              if(query[canuse[j]].b>=edge[p].b&&query[canuse[j]].a>=edge[p].a)
    81                  merge(edge[p].x,edge[p].y,edge[p].a,edge[p].b);
    82          }
    83          int x=find(query[canuse[j]].x),y=find(query[canuse[j]].y);
    84          if(x==y&&maxa[x]==query[canuse[j]].a&&maxb[y]==query[canuse[j]].b) ans[query[canuse[j]].id]=1;
    85          else ans[query[canuse[j]].id]=0;
    86          del();
    87       }
    88     }
    89     for(int i=1;i<=q;i++)
    90     {
    91       if(ans[i]) puts("Yes");
    92       else puts("No");
    93     }
    94 }
  • 相关阅读:
    k8s-[排查记录]解决节点无法查看pod日志
    k8s kube-proxy模式
    容器网络
    k8s-使用kubeadm安装集群
    k8s-Deployment重启方案
    k8s-NetworkPolicy-网络策略
    nodejs 解析终端特殊字符
    fluentd 日志自定义字段解析
    题目笔记 CF 1494b
    CF1225D Power Products(分解质因子 哈希)
  • 原文地址:https://www.cnblogs.com/qt666/p/6484264.html
Copyright © 2011-2022 走看看