P3247-[HNOI2016]最小公倍數【分塊,並查集】
正題
題目連結:https://www.luogu.com.cn/problem/P3247
題目大意
n n n個點 m m m條邊,每條邊有 ( x , y , a , b ) (x,y,a,b) (x,y,a,b)。 q q q次詢問 ( x ′ , y ′ , a ′ , b ′ ) (x',y',a',b') (x′,y′,a′,b′)表示詢問是否存在一條 x ′ − > y ′ x'->y' x′−>y′的路徑使得路徑上 a m a x = a ′ , b m a x = b ′ a_{max}=a',b_{max}=b' amax=a′,bmax=b′
解題思路
考慮暴力的做法,我們用加入所有 a ≤ a ′ a\leq a' a≤a′且 b ≤ b ′ b\leq b' b≤b′的邊加入,然後看 x ′ y ′ x'y' x′y′是否在同一連通塊且聯通塊中 a , b a,b a,b最大值是否是 a ′ , b ′ a',b' a′,b′。
如何優化,我們把所有的邊根據 a i a_i ai的權值分成若干個塊,對於每個塊我們把所有 a ′ a' a′在這個範圍的內的詢問進行處理,我們把所有處理的詢問和在塊前面的邊按照 b b b排序,然後這一部分我們就用指標掃描加入,對於在塊裡的邊我們就暴力掃描加入。
但是每個詢問做完之後注意塊裡的邊要刪去,所以我們不能用路徑壓縮,用按秩合併即可。
時間複雜度 O ( q m log n ) O(q\sqrt m\log n) O(qmlogn)
c o d e code code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cmath>
using namespace std;
const int N=1e5+10;
struct node{
int x,y,a,b,s;
}e[N],q[N],cl[N];
int n,m,Q,tot,p[N];
int fa[N],siz[N],A[N],B[N];
bool ans[N];
bool cmpa(node x,node y)
{return x.a==y.a?(x.b<y.b):(x.a<y.a);}
bool cmpb(node x,node y)
{return x.b==y.b?(x.a<y.a):(x.b<y.b);}
int find(int x)
{return fa[x]==x?x:find(fa[x]);}
int read() {
int x=0,f=1; char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar();
return x*f;
}
void unionn(int x,int y,int a,int b){
x=find(x),y=find(y);
if(siz[x]>siz[y])swap(x,y);
cl[++tot]=(node){x,y,A[y],B[y],siz[y]};
A[y]=max(max(A[x],A[y]),a);
B[y]=max(max(B[x],B[y]),b);
if(x!=y)fa[x]=y;siz[y]=max(siz[y],siz[x]+1);
return;
}
void Clear(){
for(int i=tot;i>=1;i--){
fa[cl[i].x]=cl[i].x;
A[cl[i].y]=cl[i].a;
B[cl[i].y]=cl[i].b;
siz[cl[i].y]=cl[i].s;
}
tot=0;return;
}
int main()
{
// freopen("multiple5.in","r",stdin);
// freopen("data.out","w",stdout);
n=read();m=read();
for(int i=1;i<=m;i++)
e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read();
Q=read();
for(int i=1;i<=Q;i++)
q[i].x=read(),q[i].y=read(),q[i].a=read(),q[i].b=read(),q[i].s=i;
sort(e+1,e+1+m,cmpa);sort(q+1,q+1+Q,cmpb);
int T=sqrt(m*log2(n));e[m+1].a=1e9+1;
for(int k=1;k<=m;k+=T){
int l=k,r=min(k+T,m);int cnt=0;
for(int i=1;i<=n;i++)fa[i]=i,A[i]=B[i]=-1,siz[i]=0;
for(int i=1;i<=Q;i++)
if(q[i].a>=e[l].a&&q[i].a<e[r+1].a)
p[++cnt]=i;
if(!cnt)continue;
if(k)sort(e+1,e+l,cmpb);
int pt=1;
for(int i=1;i<=cnt;i++){
int x=p[i];
while(pt<l&&e[pt].b<=q[x].b)
unionn(e[pt].x,e[pt].y,e[pt].a,e[pt].b),pt++;
tot=0;
for(int j=l;j<=r;j++)
if(e[j].a<=q[x].a&&e[j].b<=q[x].b)
unionn(e[j].x,e[j].y,e[j].a,e[j].b);
int fx=find(q[x].x),fy=find(q[x].y);
ans[q[x].s]=((fx==fy)&&(A[fx]==q[x].a)&&(B[fx]==q[x].b));
Clear();
}
}
for(int i=1;i<=Q;i++)
if(ans[i])printf("Yes\n");
else printf("No\n");
return 0;
}
相關文章
- 最小公倍數 hd 1108
- Python求最小公倍數Python
- 最大公約數,最小公倍數
- 最小公倍數&&最大公約數
- 求三個數的最小公倍數
- 最大公約數和最小公倍數
- 最小公倍數和最大公約數
- bzoj3444: 最後的晚餐(並查集+組合數學)並查集
- 用遞迴求出最大公約數和最小公倍數,求補充最小公倍數的遞迴用法遞迴
- 並查集到帶權並查集並查集
- 關押罪犯 擴充套件域並查集 帶權並查集 二分圖+二分套件並查集
- C++:最小公倍數與最大公約數C++
- NumPy 差分、最小公倍數、最大公約數、三角函式詳解函式
- (杭電1019 最小公倍數) Least Common MultipleAST
- java求最小公倍數(親測秒懂)Java
- 【數學問題】最大公約數與最小公倍數
- 查並集
- 【並查集】【帶偏移的並查集】食物鏈並查集
- 杭電OJ 2028求n個數的最小公倍數
- 並查集(一)並查集的幾種實現並查集
- 並查集(小白)並查集
- [leetcode] 並查集(Ⅱ)LeetCode並查集
- [leetcode] 並查集(Ⅲ)LeetCode並查集
- [leetcode] 並查集(Ⅰ)LeetCode並查集
- 3.1並查集並查集
- 並查集應用並查集
- 寫模板, 並查集。並查集
- 並查集的使用並查集
- 並查集跳躍並查集
- 各種並查集並查集
- 淺談並查集並查集
- 食物鏈(並查集)並查集
- 並查集(Union Find)並查集
- The Door Problem 並查集並查集
- 並查集練習並查集
- 並查集(二)並查集的演算法應用案例上並查集演算法
- L2-013 紅色警報 (25 分)(並查集)並查集
- Codeforces2020D Connect the Dots(觀察 + 並查集 + 差分)並查集