SCOI2015 国旗计划
题目链接:
断环为链,将环复制两倍。
用to[i][j]表示从点i出发,走\(2^j\)步能够到达的最远的(右端点qwq)的编号。
注意为了方便判断已经走了一圈了,我们最后一步先不要跳,只需要判断当前右端点仍在i+n之前即可。然后这个样子的话,最后答案需要+1。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 400010
using namespace std;
int n,m;
int to[MAXN][22];
struct Node{int l,r,id,ans;}t[MAXN];
inline bool cmp(struct Node x,struct Node y)
{
if(x.l!=y.l) return x.l<y.l;
return x.r<y.r;
}
inline bool cmp1(struct Node x,struct Node y){return x.id<y.id;}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&t[i].l,&t[i].r);
if(t[i].r<t[i].l) t[i].r+=m;
t[i].id=i;
}
for(int i=n+1;i<=2*n;i++)
{
t[i].l=t[i-n].l+m;
t[i].r=t[i-n].r+m;
t[i].id=i;
}
sort(&t[1],&t[n*2+1],cmp);
int pos=1;
for(int i=1;i<=n*2;i++)
{
while(pos<2*n&&t[pos+1].l<=t[i].r) pos++;
to[i][0]=pos;
}
for(int j=1;j<=21;j++)
for(int i=1;i<=2*n;i++)
to[i][j]=to[to[i][j-1]][j-1];
for(int i=1;i<=n;i++)
{
int now=i,cur_ans=1;
for(int j=21;~j;j--)
{
if(to[now][j]<i+n)
{
cur_ans+=(1<<j),now=to[now][j];
//printf("cur_ans=%d now=%d\n",cur_ans,now);
}
}
t[i].ans=cur_ans;
}
sort(&t[1],&t[2*n+1],cmp1);
for(int i=1;i<=n;i++) printf("%d ",t[i].ans);
return 0;
}