Adam Gąsienica‑Samek Contest 1-I、竞赛图、倍增
Adam Gąsienica‑Samek Contest 1-I、竞赛图、倍增
题面:https://codeforces.com/gym/104479/problem/I
题意:
-
有一张 \(n\) 个点的竞赛图,图未给出,但是对每个点 \(i\) ,知道一个 \(c_i\) 表示从 \(i\) 出发能到达几个点。
-
有 \(m\) 个可选的操作,每个操作形如 \((u_i,v_i)\) ,表示把 \((u_i,v_i)\) 之间的有向边变成无向边。
-
有 \(q\) 次询问,每次问最少需要几次操作,才能使得 \(a\) 能够到达 \(b\) 。
-
因为图的形态不确定,所以还会问最好/最坏情况下的最少操作是多少次。
\(1\leq n,m,q\leq 5\times 10^5\)。
题解:
写一些性质
竞赛图有一些性质:
-
1、竞赛图是强连通的,当且仅当存在哈密顿回路。
-
2、竞赛图一定存在哈密顿通路(归纳证明)。
-
3、如果存在环,一定存在三元环(很显然)。
-
4、竞赛图的强连通分量是一条链加上若干条边,类似这种:
dp、倍增
因此缩点完后,每个点所在的强连通分支是可以通过 \(c_i\) 来直接确定的,按照拓扑序从小到大排成一行,拓扑序小的自然可以到拓扑序大的。
把每个操作 \((u,v)\) 记在拓扑序大的点所在的连通分支上,对每个强连通分支,去dp,记 \(f[i][j]\) 表示从连通分支 \(i\) 出发,做 \(2^j\) 次操作所能到达的最左边(拓扑序最小)的连通分支是多少。
#include<bits/stdc++.h>
#define endl '\n'
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int N=5e5+5;
int n,m,q;
int bl[N],f[N][25];
pair<int,int> c[N];
int main(){
fastio;
cin>>n>>m>>q;
rep(i,1,n){
cin>>c[i].first;
c[i].second=i;
f[i][0]=i;
}
sort(c+1,c+n+1);
int lst=-1,tot=0;
for(int i=n;i>=1;i--){
if(c[i].first!=lst){
lst=c[i].first;
tot++;
}
bl[c[i].second]=tot;
}
rep(i,1,m){
int u,v;
cin>>u>>v;
u=bl[u];v=bl[v];
if(u==v)continue;
if(u<v)swap(u,v);
f[u][0]=min(f[u][0],v);
}
for(int i=tot-1;i>=1;i--)f[i][0]=min(f[i][0],f[i+1][0]);
rep(j,1,20){
rep(i,1,tot)f[i][j]=f[f[i][j-1]][j-1];
for(int i=tot-1;i>=1;i--)f[i][j]=min(f[i][j],f[i+1][j]);
}
rep(i,1,q){
char ch;
int a,b,ans=0;
cin>>ch>>a>>b;
a=bl[a];b=bl[b];
if(a>b){
for(int j=20;j>=0;j--){
if(f[a][j]>b){
ans|=(1<<j);
a=f[a][j];
}
}
if(f[a][0]<=b)ans++;
else ans=-1;
}
cout<<ans<<endl;
}
return 0;
}