把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷4155】[SCOI2015] 国旗计划(贪心+倍增)

点此看题面

  • 给定一个周长为\(m\)的圆上的\(n\)个区间(保证互不包含)。
  • 对于每个区间,求出在强制选择这个区间的前提下,至少要选择多少个区间才能将这个圆完全覆盖。
  • \(n\le2\times10^5,m\le10^9\)

贪心+倍增

考虑完全覆盖一个序列,肯定是从左往右对于每个未覆盖位置,贪心地找到包含这个位置的右端点最大的区间。

现在是在一个圆上,肯定没办法找到一个起始位置一遍贪心走人。

但这道题的询问方式实际上给了我们一定启发:在初始选中一个区间后,我们肯定是每次贪心选择左端点小于当前区间右端点的右端点最大的区间,直至将圆完全覆盖。

然后发现对于一个区间我们采取的策略并不受之前决策的影响,可以直接倍增预处理出往后跳\(2^j\)步到达的区间。

具体地,我们初始把每个区间复制一份断环为链,就可以从后往前对每个区间预处理出倍增数组。

接着,枚举每个区间倍增找到最后一个右端点小于当前区间左端点\(+m\)的区间,那么它的答案就是步数\(+2\)(当前区间以及最后一步没算入的区间)。

代码:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define LN 20
using namespace std;
int n,m,ans[N+5],f[2*N+5][LN+1];
struct Data {int p;unsigned l,r;I bool operator < (Con Data& o) Con {return l<o.l;}}s[2*N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void write(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc(' ');}
}using namespace FastIO;
int main()
{
	RI i;for(read(n,m),i=1;i<=n;++i) read(s[i].l,s[i].r),
		s[i].p=i,s[i].l>s[i].r&&(s[i].r+=m),s[i+n].l=s[i].l+m,s[i+n].r=s[i].r+m;//复制一份,断环为链
	RI j,p;for(sort(s+1,s+2*n+1),i=p=2*n;i;--i) {W(s[p].l>s[i].r) --p;for(f[i][0]=p,j=1;j<=LN;++j) f[i][j]=f[f[i][j-1]][j-1];}//排序,从后往前预处理倍增数组
	RI x,t;for(i=1;i<=n;ans[s[i++].p]=t+2) for(x=i,t=0,j=LN;~j;--j) s[f[x][j]].r<s[i].l+m&&(x=f[x][j],t|=1<<j);//对每个区间倍增求答案
	for(i=1;i<=n;++i) write(ans[i]);return clear(),0;
}
posted @ 2021-05-26 20:39  TheLostWeak  阅读(75)  评论(0编辑  收藏  举报