題目
不難發現題意就是,每條邊有兩種權值,每次詢問兩個點\(u,v\),問\(u\)到\(v\)是否存在一條路徑滿足第一類邊權的最大值為\(a\),第二類邊權的最大值為\(b\)
乙個直觀的暴力做法就是把\(a_i\leq a,b_i\leq b\)的邊都加進來,看看加入這些邊後\(u,v\)是否聯通;如果聯通,在看看\(u,v\)所在聯通塊的兩種邊權的最大值是否分別為\(a、b\);如果是,答案為yes
,否則為no
。顯然這個暴力可以使用並查集來實現。
有了暴力現在開始分塊,我們先將所有邊按照第一類權值排序,詢問按照第二類權值排序;之後分塊,對於每個塊我們維護乙個字首並查集,我們在處理一組詢問之前會將所有第二類權值不超過這個詢問的邊加入這些字首並查集中,這樣我們的詢問就只是查乙個字首,我們暴力把散塊裡的邊加入到上乙個並查集裡就好了,詢問完了再一條一條撤銷回來
時間複雜度是\(o((m+q)\sqrt\log n)\),不太會將\(\log\)放進根號裡面;經過手玩,塊大小在\(5\sqrt\)的時候跑得最快
**
#pragma gcc optimize(2)
#pragma gcc optimize(3)
#include#define re register
const int maxn=5e4+5;
const int m=1605;
inline int read()
struct edgea[maxn<<1];
struct askq[maxn];
int m,b,p[maxn<<1],id[maxn<<1],vx[maxn<<1],vy[maxn<<1],cnt,num,l[m],r[m];
unsigned short n,q,ans[maxn];
inline int max(int a,int b)
struct dsu
inline int find(int x)
inline void merge(const int &x,const int &y,const int &a,const int &b)
if(sz[xx]>1;
if(c[mid]==t) nw=mid;
if(c[mid]<=t) l=mid+1;else r=mid-1;
} return nw;
}inline void add(int t)
int main()
q[cnt].rk=i;
} int l=1,r;g[0].build();
while(l<=m)
std::sort(q+1,q+cnt+1,cxp);int lp=1;
for(re int i=1;i<=cnt;i++)
int u=p[pos]-1,sth=0;
for(re int j=l[u+1];j<=pos;j++)
if(a[j].y<=q[i].y) ++sth,g[u].merge(a[j].u,a[j].v,a[j].x,a[j].y);
ans[q[i].rk]=g[u].query(q[i].s,q[i].t,q[i].x,q[i].y);
while(sth--) g[u].back();
} for(re int i=1;i<=q;i++) puts(ans[i]?"yes":"no");return 0;
}
HNOI2016 最小公倍數
給定乙個 n 個點 m條邊的無向圖,每條邊有兩個引數 a b q 個詢問,每個詢問給s,t,a,b,求是否存在一條 s 到 t的路徑 是 路徑 而不是 簡單路徑 使得經過的邊中am ax a bma x b n,q 50000 m 105 a,b 109 暴力的想法就是對於每個詢問,只加a qa且b...
HNOI2016 最小公倍數
這是hnoi2016的day1t1 是一道眾多cj神犇口中的水題,也是ymd 用分塊,莫隊打天下的第一站 這個題只要數學沒有跪爛,應該還是可以看出來,目標是要判定是否存在路徑使x,y聯通,且路上的a的最大值等於a,b的最大值等於b 好我們先考慮暴力怎麼解決 對於前二十分,我們考慮對於每組詢問,只加入...
HNOI2016 最小公倍數
分塊,並查集 對邊 a 和詢問 b 分別排序 邊分塊處理,找出當前塊內所有的詢問一起處理 對於每乙個塊的詢問 對於前面塊內的邊,a肯定比當前詢問小,所以按b排序,一條條加入並查集,直到超過詢問的b 這一步可以雙指標維護,就不用標記了 暴力遍歷該塊,找到符合要求的邊,加入並查集,同時標記 並查集判聯通...