[Scoi2015] bzoj 4444 国旗计划 - 倍增

首先把环写两遍展开。
然后考虑必须选一个区间,只需要把这个区间作为最左边的区间开始选(因为如果左面还有区间等价于放到右面处理)。
用倍增维护这个过程即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#define N 400010
#define LOG 21
#define INF (LLONG_MAX/10-10)
#define lint long long
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
struct P{
    lint l,r;int id;
    inline bool operator<(const P &p)const { return l<p.l; }
}p[N];int nxt[N][LOG],ans[N];
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
char ss[N*20],tt[20];int ssl,ttl;
inline int show(int x)
{
    if(!x) ss[++ssl]='0';
    for(ttl=0;x;x/=10) tt[++ttl]=x%10+'0';
    for(;ttl;ttl--) ss[++ssl]=tt[ttl];
    return ss[++ssl]=' ';
}
int main()
{
    int n=inn(),m=inn();
    for(int i=1;i<=n;i++)
    {
        int x=inn(),y=inn();if(x>y) y+=m;p[i+n].id=0;
        p[i].id=i,p[i+n].l=(p[i].l=x)+m,p[i+n].r=(p[i].r=y)+m;
    }
    sort(p+1,p+2*n+1),p[2*n+1].l=INF;
    for(int i=2*n,j=2*n+1;i;i--)
    {
        while(j&&p[j].l>p[i].r) j--;
        if(p[j].l<=p[i].r) nxt[i][0]=j;
    }
    for(int j=1;j<LOG;j++)
        for(int i=1;i<=2*n;i++)
            nxt[i][j]=nxt[nxt[i][j-1]][j-1];
    for(int i=1;i<=2*n;i++) if(p[i].id)
        for(int j=LOG-1,x=i;j>=0;j--)
            if(nxt[x][j]&&p[nxt[x][j]].r<p[i].l+m)
                x=nxt[x][j],ans[p[i].id]+=1<<j;
    for(int i=1;i<=n;i++) show(ans[i]+2);ss[++ssl]='\n';
    return fwrite(ss+1,sizeof(char),ssl,stdout),0;
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/81945028