考场爆零很是不爽……
好吧讲道理这题其实很裸
4537: [Hnoi2016]最小公倍数
Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 575 Solved: 243
[ Submit][ Status][ Discuss]
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
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
No
No
HINT
Source
HOME Back
首先我们会想到一个暴力做法,那就是对于每个询问,插入所有满足a不大于询问的a,且b不大于询问的b的边并判断是否u和v是否在一个a的最大值即询问的a,b的最大值即询问的b的连通块内。显然这样做是O(mq)的,显然要TLE。
那么想到这就不难想到分块了。首先我们对边直接分块,先关于权值a排序,每块的大小是m^0.5,共有m^0.5个块。
然后我们可以将询问分配到每个块中。对于每个块中的所有询问,我们考虑对这个块以前的所有边暴力按b排序,因为这些边权显然都满足a比询问的a小,对于一个块中的询问可以在O(m log m)的时间内解决这部分的边,可以将它们插入并查集。
然后还剩下一些零零散散的边,即在询问所在的权值块内,由于边只有m^0.5条,即使我们对于所有的询问暴力枚举查找可以加的边也是O(m^0.5)每个询问的,这时我们就要考虑如何使并查集在加了边之后又能回到之前的状态。
其实很简单,我们知道并查集如果按秩合并的话最坏的高度也就只有logn层(完全二叉树),因此我们可以考虑不进行路径压缩,只是按秩合并,将修改前的记录保存入一个栈内,每次询问结束时复原即可。
时间复杂度 O((m log m+q log n)m^0.5)
/**************************************************************
Problem: 4537
User: whzzt
Language: C++
Result: Accepted
Time:11088 ms
Memory:19648 kb
****************************************************************/
#include "stdio.h"
#include "algorithm"
#include "iostream"
#include "string.h"
#include "stdlib.h"
#include "math.h"
#include "vector"
#include "map"
#include "set"
using namespace std;
const int L=10000005;
char _buff[L]; int _pos=-1;
void ReadIn(){fread(_buff,L,sizeof(char),stdin);}
#define fge _buff[++_pos]
inline int read(){
int x=0; char ch=fge;
while(ch>'9'||ch<'0')ch=fge;
while(ch<='9'&&ch>='0')
x=x*10+ch-'0',ch=fge;
return x;
}
const int N=50005,M=100005;
struct E{int u,v,a,b;} e[M],q[M],st[M],es[M];
inline bool cmpa(const E&a,const E&b)
{ return a.a==b.a?a.b<b.b:a.a<b.a;}
inline bool cmpb(const E&a,const E&b)
{ return a.b==b.b?a.a<b.a:a.b<b.b;}
inline bool cmpv(const int&u,const int&v)
{ E a=q[u],b=q[v];return a.b==b.b?a.a<b.a:a.b<b.b;}
inline void init(E&a){a.u=read(),a.v=read(),a.a=read(),a.b=read();}
int n,m,Q,lim,top,ans[N],l,r,la,in[N];
int fa[N],siz[N],maxa[N],maxb[N],val[N];
int tmp,rq[N],sz[N],ma[N],mb[N],p[N];
int find(int x){return fa[x]==x?x:find(fa[x]);}
void merge(E a,int type){
int fu=find(a.u),fv=find(a.v);
if(siz[fu]>siz[fv])swap(fu,fv);
if(type){
rq[++tmp]=fv;sz[tmp]=siz[fv];p[tmp]=fu;
ma[tmp]=maxa[fv];mb[tmp]=maxb[fv];
}
if(fu!=fv){
fa[fu]=fv;siz[fv]+=siz[fu];
maxa[fv]=max(maxa[fu],maxa[fv]);
maxb[fv]=max(maxb[fu],maxb[fv]);
}
maxa[fv]=max(maxa[fv],a.a);
maxb[fv]=max(maxb[fv],a.b);
}
int main(){
ReadIn();n=read(),m=read();int i,j,k;
for(i=1;i<=m;i++)init(e[i]);Q=read();
for(i=1;i<=Q;i++)init(q[i]);lim=(int)sqrt(m+.1);
sort(e+1,e+m+1,cmpa);
for(i=0;i<=m;i+=lim){
l=i+1,r=min(i+lim,m);
for(j=1,top=0;j<=Q;j++)
if(q[j].a>=e[l].a&&(q[j].a<e[r].a||(r==m||e[r].a<e[r+1].a)&&e[r].a==q[j].a))
val[++top]=j;
if(top==0)continue;
sort(val+1,val+top+1,cmpv);
for(j=1;j<=top;j++)st[j]=q[val[j]];
for(j=1;j<=n;j++)fa[j]=j,maxa[j]=maxb[j]=-1,siz[j]=1;
memcpy(es+1,e+1,sizeof(E)*i);sort(es+1,es+i+1,cmpb);
for(k=la=1;k<=top;k++){
for(;la<=i;la++)
if(es[la].b<=st[k].b)
merge(es[la],0);
else break;
for(j=l;j<=r;j++){
if(e[j].a<=st[k].a&&e[j].b<=st[k].b)
merge(e[j],1);
} j=find(st[k].u);
if(maxa[j]==st[k].a&&maxb[j]==st[k].b&&j==find(st[k].v))ans[val[k]]=1;
for(;tmp;tmp--){
fa[p[tmp]]=p[tmp];siz[rq[tmp]]=sz[tmp];
maxa[rq[tmp]]=ma[tmp];maxb[rq[tmp]]=mb[tmp];
p[tmp]=rq[tmp]=ma[tmp]=mb[tmp]=sz[tmp]=0;
}
}
}
for(i=1;i<=Q;i++)puts(ans[i]?"Yes":"No");
return 0;
}