[HNOI2016] 最小公倍數

P4053 [Hnoi2016 day1]最小公倍數
時間限制 : - MS   空間限制 : 565536 KB 
評測說明 : 4s
問題描述

給定一張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,那麼稱路徑爲簡單路徑。

輸入格式

第一行包含兩個整數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

輸出格式

對於每次詢問,如果存在滿足條件的路徑,則輸出一行Yes,否則輸出一行 No(注意:第一個字母大寫,其餘
字母小寫) 。

樣例輸入

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

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

樣例輸出

Yes 
Yes 
Yes 
No 
No 

提示

樣例說明:

分析:首先不難發現所謂的lcm就是路徑上a的最大值和b的最大值,那麼暴力方法就出來了,選出所有a不大於q.a並且b不大於q.b的邊,跑帶權並查集,暴力驗證。

不bb,說正解。

先把m條邊按a排序並分塊,q次詢問按b排序。

對於每一塊,我們只處理a在此塊的邊的a的範圍內的詢問,把這些詢先弄出來。

此時,我們再把之前的塊的邊按b排序。

由於這些詢問的a一定大於前面塊的邊的a,即前面的塊中的邊的a一定滿足限制,只需滿足b。

並查集,要回退,回退的操作是哪些呢?一個詢問在這個塊內滿足條件的邊。之前塊的邊是不需要回退的,因爲a滿足,詢問的b遞增,所以之前加進來的邊後面的詢問一定可以用!

詳細見代碼吧,還是比較好懂。

#include<cstdio>  
#include<iostream>  
#include<cstdlib>  
#include<algorithm>  
#include<cstring>  
#include<queue>  
#include<stack>  
#include<vector> 
#include<cmath>  
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
template <typename T>  
inline void _read(T& x){  
    char t=getchar();bool sign=true;  
    while(t<'0'||t>'9'){if(t=='-')sign=false;t=getchar();}  
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';  
    if(!sign)x=-x;  
}
int n,m,q,e;
int block;
int top;
int tot;
struct node{
	int x,y,a,b,id;
	node(){}
	node(int X,int Y,int A,int B,int ID){x=X;y=Y;a=A;b=B;id=ID;}
};
bool cmp1(node A,node B){
	if(A.a==B.a)return A.b<B.b;
	else return A.a<B.a;
}
bool cmp2(node A,node B){
	if(A.b==B.b)return A.a<B.a;
	else return A.b<B.b;
}
node edge[100005];
node work[100005];
int need[100005];
int fa[100005],maxa[100005],maxb[100005],size[100005];
bool ans[100005];
struct back{
	int x,y,fa,ma,mb,si;
	back(){}
	back(int X,int Y,int FA,int MA,int MB,int SI){x=X;y=Y;fa=FA;ma=MA;mb=MB;si=SI;}
};
back roll[100005];
int getfather(int x){
	if(fa[x]!=x)return getfather(fa[x]);
	else return x;
}
void merge(int x,int y,int a,int b){
	int fx=getfather(x),fy=getfather(y);
	if(size[fx]>size[fy])swap(fx,fy);
	roll[++top]=back(fx,fy,fa[fx],maxa[fy],maxb[fy],size[fy]);
	if(fx==fy){
		maxa[fx]=max(maxa[fx],a);
		maxb[fx]=max(maxb[fx],b);
		return;
	}
	fa[fx]=fy;
	size[fy]+=size[fx];
	maxa[fy]=max(maxa[fy],max(a,maxa[fx]));
	maxb[fy]=max(maxb[fy],max(b,maxb[fx]));
}
void rollback(){
	int i;
	for(i=top;i;i--){
		fa[roll[i].x]=roll[i].fa;
		maxa[roll[i].y]=roll[i].ma;
		maxb[roll[i].y]=roll[i].mb;
		size[roll[i].y]=roll[i].si;
	}
	top=0;
}
int main(){
	int i,j,k;
	cin>>n>>m;
	for(i=1;i<=m;i++){
		_read(edge[i].x);_read(edge[i].y);_read(edge[i].a);_read(edge[i].b);
	}
	cin>>q;
	for(i=1;i<=q;i++){
		_read(work[i].x);_read(work[i].y);_read(work[i].a);_read(work[i].b);
		work[i].id=i;
	}
	sort(edge+1,edge+1+m,cmp1);
	sort(work+1,work+1+q,cmp2);
	block=755;
	for(i=1;i<=m;i+=block){
		tot=top=0;
		for(j=1;j<=q;j++){
			if(work[j].a>=edge[i].a&&(i+block>m||work[j].a<edge[i+block].a)){
				need[++tot]=j;
			}
		}
		sort(edge+1,edge+i,cmp2);
		for(j=1;j<=n;j++){
			fa[j]=j;maxa[j]=-1;maxb[j]=-1;size[j]=1;
		}
		int pos=1;
		for(j=1;j<=tot;j++){
			while(pos<i&&work[need[j]].b>=edge[pos].b)merge(edge[pos].x,edge[pos].y,edge[pos].a,edge[pos].b),pos++;
			top=0;
			for(k=i;k<i+block&&k<=m;k++){
				if(edge[k].a<=work[need[j]].a&&edge[k].b<=work[need[j]].b)merge(edge[k].x,edge[k].y,edge[k].a,edge[k].b);
			}
			int x,y;
			x=getfather(work[need[j]].x);y=getfather(work[need[j]].y);
			if(x==y&&maxa[x]==work[need[j]].a&&maxb[x]==work[need[j]].b)ans[work[need[j]].id]=true;
			else ans[work[need[j]].id]=false;
			rollback();
		}
	}
	for(i=1;i<=q;i++){
		if(ans[i])puts("Yes");
		else puts("No");
	}
}



發佈了195 篇原創文章 · 獲贊 14 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章