注意看,我耗時五個小時 AK 了 IOI
題意
給你一個圖,每次給定若干詢問 \((s,t,l,r)\),請你完成下述要求:
- 定義 \(S\) 為到 \(s\) 的最短路徑不小於 \(l\) 的點構成的子圖,\(T\) 為到 \(t\) 的最短路徑不大於 \(r\) 的點構成的子圖
- 請你判斷 \(S\) 與 \(T\) 是否有交集
解法
當詢問次數不大時做法顯然,每次對 \(s,t\) 跑 dfs 即可
但是此題詢問量大,考慮如何預處理
一 尋找子圖
路徑最小最大,容易想到使用 Kruskal 生成樹. 我們嘗試對整張圖建立 Kruskal 最小和最大生成樹,根據其性質在樹上跑 LCA 來 \(\log\) 解決這部分問題.
但是在實際實現的時候會遇到一些問題:我們仍然還需要列舉所有點,因此最壞情況下需要單次 \(n\log n\) 求解,複雜度不夠優秀.
現在轉化問題:注意到我們求解出來的子圖一定是樹結構,由 dfs 序的性質,最終這顆子樹一定是原樹 dfs 序上連續的一段,因此我們將問題抽象成這樣:
給定兩個序列 \(S,T\),詢問是否存在 \(i\in[l,n]\),使得存在 \(T_{j}=S_{i},j\in[1,r]\)
其中 \(S\) 是我們建立的 Kruskal 最小生成樹的 dfs 序,\(T\) 是建立的 Kruskal 最大生成樹的 dfs 序,這樣我們就透過兩遍 Kruskal+LCA 轉化了這個問題
二 求解交集
注意到我們可以建立一個陣列 \(f_{i}\) 表示 \(i\) 在 \(S\) 中的位置,這樣建立一個對映是為了方便後續操作:我們可以透過呼叫 \(f_{T_{i}}\) 來檢視 \(T_{i}\) 在 \(S\) 中的位置
因此,我們可以進一步轉化這個問題:
給定兩個序列 \(S,T\),詢問是否存在 \(i\in[1,r]\),使得 \(f_{T_{i}}\in[l,n]\)
注意到我們現在把問題轉化成了一種類似求值域的東西,因此考慮對 \(f_{T_{i}}\) 開可持久化線段樹,維護字首和,每次把 \(r,l-1\) 兩顆可持久化線段樹傳下去作差維護答案,這個問題即可求解.
下面是這一部分,可持久化線段樹內的 ask() 函式程式碼
bool ask(int p,int q,int l,int r,int L,int R){ //存在則返回 true
if(l>R or L>r) return false;
if(L<=l and r<=R) return t[q].cnt-t[p].cnt; //滿足[1,r]條件後還需要作差
int mid(l,r);
return ask(t[q].l,t[p].l,l,mid,L,R) or ask(t[q].r,t[p].r,mid+1,r,L,R);
}
比較簡單。
三 整合
那我們剛才弄出來的 Kruskal 生成樹顯然就是用來找,我們需要的 dfs 序的左右端點(也即哪兩顆可持久化線段樹需要被傳下去)了,這一部分暴力倍增即可。
注意事項
- 在 loj 版裡,需要引用
"werewolf.h"
- 求端點的倍增別弄反了,應該是先大再小
- 因為用了 Kruskal 生成樹,因此空間需要開到 \(2n\)
程式碼
#include<bits/stdc++.h>
#include"werewolf.h"
using namespace std;
const int inf=0x7fffffff;
int n,m,q;
struct edge{
int from,to;
};
edge ed[400001];
int a[400001],b[400001];
int cnt=0;
bool cmp1(const edge &A,const edge &B){
return max(A.from,A.to)<max(B.from,B.to);
}
bool cmp2(const edge &A,const edge &B){
return min(A.from,A.to)>min(B.from,B.to);
}
int mp[400001],t[400001],root[400001];
class dsu{
private:
int fa[400001];
public:
void clear(){
for(int i=1;i<=2*n;++i){
fa[i]=i;
}
}
int find(int id){
if(id==fa[id]) return id;
fa[id]=find(fa[id]);
return fa[id];
}
void join(int x,int y){ //add x to y (fa[x]=y)
int fx=find(x),fy=find(y);
if(fx!=fy){
fa[fx]=fy;
}
}
};
class kruskal{
public:
int tot,treecnt;
int fa[20][400001],w[400001],l[400001],r[400001];
vector<int>e[400001];
dsu d;
void clear(){
tot=n;
d.clear();
memset(l,0x3f,sizeof l);
}
void dfs(int now,int type){
if(now<=n){
if(type==1){
a[++treecnt]=now;
}
else{
b[++treecnt]=now;
}
l[now]=r[now]=treecnt;
return;
}
for(int i:e[now]){
if(i==fa[0][now]) continue;
fa[0][i]=now;
dfs(i,type);
l[now]=min(l[now],l[i]);
r[now]=max(r[now],r[i]);
}
}
void build(int type){
clear();
if(type==1){
sort(ed+1,ed+m+1,cmp1);
}
else{
sort(ed+1,ed+m+1,cmp2);
}
for(int i=1;i<=m;++i){
if(d.find(ed[i].from)!=d.find(ed[i].to)){
e[++tot].push_back(d.find(ed[i].from));
e[tot].push_back(d.find(ed[i].to));
e[d.find(ed[i].from)].push_back(tot);
e[d.find(ed[i].to)].push_back(tot);
d.join(ed[i].from,tot);
d.join(ed[i].to,tot);
if(type==1){
w[tot]=max(ed[i].from,ed[i].to);
}
else{
w[tot]=min(ed[i].from,ed[i].to);
}
}
}
if(type==1) w[0]=inf;
else w[0]=-inf;
dfs(tot,type);
for(int j=1;j<=19;++j){
for(int i=1;i<=tot;++i){
fa[j][i]=fa[j-1][fa[j-1][i]];
}
}
}
int get_version(int now,int l,int r){
for(int i=19;~i;--i){
if(w[fa[i][now]]>=l and w[fa[i][now]]<=r){
now=fa[i][now];
}
}
return now;
}
}t1,t2;
class HIST_Stree{
public:
#define mid(l,r) mid=((l)+(r))/2
int tot=0;
struct tree{
int l,r;
int cnt;
}t[400001*25];
int clone(int id){
t[++tot]=t[id];
return tot;
}
int insert(int id,int l,int r,int pos){
int q=clone(id);
if(l==r){
t[q].cnt++;
return q;
}
int mid(l,r);
if(pos<=mid){
t[q].l=insert(t[id].l,l,mid,pos);
}
else{
t[q].r=insert(t[id].r,mid+1,r,pos);
}
t[q].cnt=t[t[q].l].cnt+t[t[q].r].cnt;
return q;
}
bool ask(int p,int q,int l,int r,int L,int R){
if(l>R or L>r) return false;
if(L<=l and r<=R) return t[q].cnt-t[p].cnt;
int mid(l,r);
return ask(t[q].l,t[p].l,l,mid,L,R) or ask(t[q].r,t[p].r,mid+1,r,L,R);
}
}hist;
vector<int> check_validity(int N, vector<int> X, vector<int> Y, vector<int> U, vector<int> V, vector<int> L, vector<int> R) {
n=N,m=X.size(),q=U.size();
vector<int>ans;
for(int i=1;i<=m;++i){
ed[i]={X[i-1]+1,Y[i-1]+1};
}
t1.build(1);
t2.build(2);
for(int i=1;i<=n;++i){
t[a[i]]=i;
}
for(int i=1;i<=n;++i){
mp[i]=t[b[i]];
}
for(int i=1;i<=n;++i){
root[i]=hist.insert(root[i-1],1,n,mp[i]);
}
for(int i=1;i<=q;++i){
int s=U[i-1]+1,t=V[i-1]+1;
int l=L[i-1]+1,r=R[i-1]+1;
swap(s,t);
s=t1.get_version(s,1,r);
t=t2.get_version(t,l,n);
if(hist.ask(root[t2.r[t]],root[t2.l[t]-1],1,n,t1.l[s],t1.r[s])){
ans.push_back(1);
}
else{
ans.push_back(0);
}
}
return ans;
}
// vector<int> X,Y,U,V,L,R;
// int main(){
// cin>>n>>m>>q;
// for(int i=1;i<=m;++i){
// int a,b;
// cin>>a>>b;
// X.push_back(a);Y.push_back(b);
// }
// for(int i=1;i<=q;++i){
// int s,e,l,r;
// cin>>s>>e>>l>>r;
// U.push_back(s);
// V.push_back(e);
// L.push_back(l);
// R.push_back(r);
// }
// vector<int> ans=check_validity(n,X,Y,U,V,L,R);
// for(int i:ans){
// cout<<i<<endl;
// }
// }