[SCOI2015]国旗计划
首先考虑一下环形的区间覆盖问题怎么做
我们可以把环倍长成链,之后惊讶的发现我只会枚举一个\(i\)作为起点跑\([i,i+m]\)的区间覆盖
看起来非常垃圾,但是会这样做就够了
考虑枚举到的这个\(i\)作为一个某一个给定的区间的左端点的时候,想要覆盖\([i,i+m]\)这段区间这个给定的区间是必须选择的,于是我们对于每一个给定的区间\([l_i,r_i]\),来算一下覆盖\([l_i,l_i+m]\)的最少要用多少个区间即可
这是一个非常经典的贪心,我们把区间按照左端点排序,贪心地选择左端点不超过当前区间右端点中右端点最大的即可
题目中保证了任意两个区间不相互包含,也就是说我们按照左端排序之后右端点也是单调的,我们只需要利用单调性就可以求出每个区间的最优转移了
对于一个区间\([l_i,r_i]\),我们顺着最优转移走,直到当前区间的右端点不小于\(l_i+m\),那么\([l_i,l_i+m]\)就被完全覆盖了,答案就是中间经过的区间个数
考虑让每个区间向其最优转移连边,这样我们就得到了一棵树,我们只需要在树上倍增一下就能快速计算了
代码
#include<bits/stdc++.h>
#define re register
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=4e5+5;
struct E{int v,nxt;}e[maxn];
struct Seg{int l,r,rk;}a[maxn];
int n,m,sz,T;
int head[maxn],num,id[maxn],lg[maxn];
int f[20][maxn],deep[maxn];
inline void add(int x,int y) {
e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
inline int cmp(const Seg &A,const Seg &B) {return A.l<B.l;}
void dfs(int x) {
for(re int i=1;i<=lg[deep[x]];++i)
f[i][x]=f[i-1][f[i-1][x]];
for(re int i=head[x];i;i=e[i].nxt) {
deep[e[i].v]=deep[x]+1;
f[0][e[i].v]=x;dfs(e[i].v);
}
}
int main() {
n=read(),m=read();T=m;m=0;
for(re int L,R,i=1;i<=n;i++) {
L=read(),R=read();
if(L<=R)
a[++m].l=L,a[m].r=R,a[m].rk=i,a[++m].l=L+T,a[m].r=R+T;
else a[++m].l=L,a[m].r=R+T,a[m].rk=i,a[++m].l=L+T,a[m].r=T+T;
}
std::sort(a+1,a+m+1,cmp);
for(re int i=1;i<=m;i++) id[a[i].rk]=i;
for(re int i=2;i<=m;++i) lg[i]=lg[i>>1]+1;
for(re int now=1,i=1;i<=m;i++) {
while(now<m&&a[now+1].l<=a[i].r) ++now;
if(i!=now) add(now,i);else deep[i]=1,dfs(i);
}
for(re int i=1;i<=n;++i) {
int x=id[i];int to=a[x].l+T;
if(a[x].r>=to) {putchar('1');putchar(' ');continue;}
for(re int j=lg[deep[x]];j>=0;--j)
if(f[j][x]&&a[f[j][x]].r<to) x=f[j][x];
printf("%d ",deep[id[i]]-deep[f[0][x]]+1);
}
return 0;
}