[HNOI2016]最小公倍数

题目描述

给定一张N个顶点M条边的无向图(顶点编号为1,2,...,n),每条边上带有权值。所有权值都可以分解成2a∗3b2^a*3^b2a3b 的形式。

现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公倍数为2a∗3b2^a*3^b2a3b 。

注意:路径可以不是简单路径。下面是一些可能有用的定义:最小公倍数:K个数a1,a2,...,ak的最小公倍数是能被每个ai整除的最小正整数。

路径:路径P:P1,P2,...,Pk是顶点序列,满足对于任意1<=i<k,节点Pi和Pi+1之间都有边相连。简单路径:如果路径P:P1,P2,...,Pk中,对于任意1≤s≠t≤k1le s e tle k1stk 都有Ps≠PtPs e PtPsPt ,那么称路径为简单路径。

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1: 复制
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
输出样例#1: 复制
Yes 
Yes 
Yes 
No 
No
对于每个询问,我们可以把所有(a,b)小于等于的边加入并查集
并查集维护联通块的最大a和最大b,分别为Maxa,Maxb
如果最大(Maxa,Maxb)=(a,b)
暴力显然不行
现将边按a排序,分块
将询问按b排序

每一块加入a值在这一块的范围内的询问,即大于等于a[i]小于a[i+√m]
前面的块里的边显然a值都符合需求,按b排序
从前往后枚举加入的询问,将前面块里小于b的边加入
因为满足当前询问的边,必定也满足下一个询问,所以不需要撤销
然后枚举当前块里的边加入,处理完当前询问要撤销并查集的操作
因为撤销操作需要储存状态并复原,只有根号个撤销操作,复杂度有保证
不能用路径压缩
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 struct Node
  8 {
  9   int u,v,a,b,id;
 10 }e[100001],q[50001],pre[100001],p[50001];
 11 int set[50001],tot,Maxa[50001],Maxb[50001],n,m,lim,r,cnt,ans[50001],size[50001];
 12 int gi()
 13 {
 14   char ch=getchar();
 15   int x=0;
 16   while (ch<'0'||ch>'9') ch=getchar();
 17   while (ch>='0'&&ch<='9')
 18     {
 19       x=(x<<3)+(x<<1)+ch-'0';
 20       ch=getchar();
 21     }
 22   return x;
 23 }
 24 bool cmpa(Node x,Node y)
 25 {
 26   if (x.a==y.a) return x.b<y.b;
 27   return x.a<y.a;
 28 }
 29 bool cmpb(Node x,Node y)
 30 {
 31   if (x.b==y.b) return x.a<y.a;
 32   return x.b<y.b;
 33 }
 34 int find(int x)
 35 {
 36   if (set[x]==x) return x;
 37   if (set[x]!=x) return find(set[x]);
 38 }
 39 void merge(Node E)
 40 {
 41   int p=find(E.u),q=find(E.v);
 42   if (size[p]<size[q]) swap(p,q);
 43   ++tot;
 44   pre[tot].u=p,pre[tot].v=q,pre[tot].a=Maxa[p],pre[tot].b=Maxb[p];pre[tot].id=size[p];
 45   if (p!=q)
 46     {
 47       set[q]=p;
 48       size[p]+=size[q];
 49     }
 50   if (Maxa[p]<E.a)
 51     Maxa[p]=E.a;
 52   if (Maxa[p]<Maxa[q])
 53     Maxa[p]=Maxa[q];
 54   if (Maxb[p]<E.b)
 55     Maxb[p]=E.b;
 56   if (Maxb[p]<Maxb[q])
 57     Maxb[p]=Maxb[q];
 58 }
 59 void undo()
 60 {int i;
 61   Node E;
 62   for (;tot;tot--)
 63     {
 64       E=pre[tot];
 65       set[E.v]=E.v;
 66       Maxa[E.u]=E.a;
 67       Maxb[E.u]=E.b;
 68       size[E.u]=E.id;
 69     }
 70 }
 71 int main()
 72 {int i,j,k,l,ed,p1,p2;
 73   cin>>n>>m;
 74   lim=sqrt(m);
 75   for (i=1;i<=m;i++)
 76     {
 77       e[i].u=gi();e[i].v=gi();e[i].a=gi();e[i].b=gi();
 78       e[i].id=i;
 79     }
 80   cin>>r;
 81   for (i=1;i<=r;i++)
 82     {
 83       q[i].u=gi();q[i].v=gi();q[i].a=gi();q[i].b=gi();
 84       q[i].id=i;
 85     }
 86   sort(e+1,e+m+1,cmpa);
 87   sort(q+1,q+r+1,cmpb);
 88   for (i=1;i<=m;i+=lim)
 89     {
 90       cnt=0;tot=0;
 91       k=1;
 92       for (j=1;j<=r;j++)
 93     if (q[j].a>=e[i].a&&(i+lim>m||q[j].a<e[i+lim].a))
 94       p[++cnt]=q[j];
 95       if (cnt==0) continue;
 96       sort(e+1,e+i,cmpb);
 97       if (i+lim-1<=m) ed=i+lim-1;
 98       else ed=m;
 99       for (j=1;j<=n;j++)
100     set[j]=j,Maxa[j]=-1,Maxb[j]=-1,size[j]=1;
101       for (j=1;j<=cnt;j++)
102     {
103       for (;k<i;k++)
104         if (p[j].b>=e[k].b) merge(e[k]);
105         else break;
106       tot=0;
107       for (l=i;l<=ed;l++)
108         if (p[j].b>=e[l].b&&p[j].a>=e[l].a)
109           {
110         merge(e[l]);
111           }
112         else if (e[l].a>p[j].a) break;
113       p1=find(p[j].u),p2=find(p[j].v);
114       if (p1==p2&&Maxa[p1]==p[j].a&&Maxb[p1]==p[j].b)
115         ans[p[j].id]=1;
116       undo();
117     }
118     }
119   for (i=1;i<=r;i++)
120     if (ans[i])
121       puts("Yes");
122     else puts("No");
123 }
原文地址:https://www.cnblogs.com/Y-E-T-I/p/8516600.html