IOI2021集训队作业 297AK Surveillance
一个长度为\(n\)的环,有\(k\)条弧。选择最少的弧覆盖整个环。
\(n,k\le 10^6\)
考虑暴力:枚举第一条弧,然后贪心选下一条,选择左端点小于等于当前弧右端点加一,且右端点最大的弧。一直如此做下去直到覆盖了整个环为止。
记\(x\)的下一条为\(p_x\),可以预处理出来。
网上普遍的做法是用倍增,即枚举每个初始弧,然后倍增出至少要跑多少次才能覆盖整个环。时间\(O(k\lg k)\)。
我的做法是连边\((x,p_x)\),在形成的环套树森林中找出每个环,对于每个环任意选择一条初始弧跑。时间\(O(k)\)。
口胡一下其正确性:显然,如果得到了最优的选择弧的集合\(S\),那么从任意弧\(x\in S\)出发跑,都可以跑出最优解;那么假如从\(x\)出发跑可以获得最优解,那么从\(p_x\)出发跑也可以获得最优解;于是可以发现,环上存在最优的初始弧,然后推出环上的每条边都是最优的初始弧。所以任意选一条初始弧即可。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1000005
int n,k;
int l[N*2],r[N*2];
int s[N*2],t[N*2];
bool cmps(int a,int b){return l[a]<l[b];}
bool cmpt(int a,int b){return r[a]<r[b];}
int p[N*2];
void build(){
for (int i=1;i<=k;++i) l[i+k]=l[i]+n,r[i+k]=r[i]+n;
for (int i=1;i<=k*2;++i) s[i]=t[i]=i;
sort(s+1,s+k*2+1,cmps),sort(t+1,t+k*2+1,cmpt);
int mx=0;
for (int i=1,j=1;i<=k*2;++i){
for (;j<=k*2 && l[s[j]]<=r[t[i]]+1;++j){
if (r[s[j]]>r[mx])
mx=s[j];
}
if (r[mx]>r[t[i]])
p[t[i]]=mx;
}
for (int i=1;i<=k;++i){
int c=0;
if (p[i]==0)
c=p[i+k];
else if (p[i+k]==0)
c=p[i];
else
c=(r[p[i]]-r[i]>r[p[i+k]]-r[i+k]?p[i]:p[i+k]);
if (c>k)
c-=k;
p[i]=c;
// int x=p[i],y=p[i+k];
// x=(x>k?x-k:x);
// y=(y>k?y-k:y);
// if (x==0) p[i]=y;
// else if (y==0) p[i]=x;
// else p[i]=((r[x]-r[i]+n+n)%n>(r[y]-r[i]+n+n)%n?x:y);
}
}
int ans;
int vis[N],cnt,bz[N];
void dfs(int x){
vis[x]=++cnt;
bz[x]=1;
if (bz[p[x]]){
bz[x]=0;
int s=1;
for (int y=p[x];y!=x;y=p[y]){
++s;
int tmp=l[x]-1;
if (tmp==0) tmp=n;
if (r[y]<n?(l[y]<=tmp && tmp<=r[y]):(tmp<=r[y]-n || tmp>=l[y]))
break;
}
ans=min(ans,s);
return;
}
if (vis[p[x]]){
bz[x]=0;
return;
}
dfs(p[x]);
bz[x]=0;
}
int c[N];
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d%d",&n,&k);
for (int i=1;i<=k;++i){
scanf("%d%d",&l[i],&r[i]);
if (l[i]>r[i]){
c[1]++,c[r[i]+1]--;
c[l[i]]++;
r[i]+=n;
}
else
c[l[i]]++,c[r[i]+1]--;
if (r[i]-l[i]+1==n){
printf("1\n");
return 0;
}
}
for (int i=1;i<=n;++i){
c[i]+=c[i-1];
if (!c[i]){
printf("impossible\n");
return 0;
}
}
build();
ans=k+1;
for (int i=1;i<=k;++i){
if (!vis[i])
dfs(i);
}
printf("%d\n",ans);
return 0;
}