P4155 [SCOI2015] 国旗计划——倍增加速贪心
先看数据范围,
破环为链这个比较自然,但是对于如何求解并没有思路
苦思冥想没有思路,直接打暴力
枚举以每一个士兵为开始,对右侧进行最小次数区间覆盖
考虑贪心,如果加入一个区间,那么这个区间在不会制造出空隙的情况下应该尽可能右端点靠右
每一次时间复杂度
让我们来重新审视贪心的这个过程,事实上是不断寻找并加入新的最优区间的过程
可以发现,对于已经选择了的最右端的区间来说,它接下来选择的最优区间是确定的
那么,我们就可以对这个进行预处理
预处理之后,我们发现每次的贪心中除了可以一次次地跳跃,还可以一次跳很多
考虑倍增维护,像LCA一样寻找填充完目标区间完前的那个区间,途中记录跳过的区间总数
时间复杂度
(区间端点记得弄清楚)
AC Code
#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int nxt[MAXN<<1][25],ans[MAXN];
int n,m,top=0;
struct RANGE{
int l,r,idx;
}range[MAXN<<1];
bool cmp(RANGE x,RANGE y){
return x.l<y.l;
}//
void getnxt(){
int j=1,r;
for(int i=1;i<=top;++i){
r=range[i].r;
while(j+1<=top && range[j+1].l<=r)
++j;
nxt[i][0]=j;
}
}//
void getst(){
for(int i=1;(1<<i)<=top;++i)
for(int j=1;j<=top;++j)
nxt[j][i]=nxt[nxt[j][i-1]][i-1];
}//
int query(int idx){
int R=range[idx].l+m,res=2;
for(int i=21;i>=0;--i){
if(!nxt[idx][i]) continue;
if(range[nxt[idx][i]].r<R){
idx=nxt[idx][i];
res+=(1<<i);
}
}
return res;
}//
void work(){
scanf("%d%d",&n,&m);
for(int i=1,l,r;i<=n;++i){
scanf("%d%d",&l,&r);
if(l>r) r+=m;
range[++top].idx=i;
range[top].l=l;range[top].r=r;
// if(r<=m){
range[++top].l=l+m;
range[top].r=r+m;
// }
}
sort(range+1,range+1+top,cmp);
getnxt();
getst();
for(int i=1;i<=top;++i){
if(range[i].idx){
ans[range[i].idx]=query(i);
}
}
for(int i=1;i<=n;++i){
printf("%d ",ans[i]);
}
}
int main(){
work();
return 0;
}