BZOJ 4444 [Scoi2015]国旗计划

破环为链+离散化+倍增

环上问题一般转化为链上问题。本题破环为链之后再在末尾复制一条链,那么一个环上覆盖就等价于一个i~i+m的链上覆盖。

对每一个点维护从它的左边最远能跳到的右边的点。发现这是一个树形结构。因此对于每一个询问i,倍增从r[i]往上跳找到第一个点j满足j≥l[i]+m。这样O(n*log^2)

还注意到每个人的答案之差最多为1。假设不强制取任何人,此时最优解为A,那么显然强制取一个人的解是A或A+1。所以只要判一两个解是否合法即可。这样O(n*log)

#include<cstdio>
#include<algorithm>
#define N 400005
#define H 20 
using namespace std;
namespace runzhe2000
{
    const int INF = 1<<30;
    int n, m, arr[N], arr_cnt, l[N], r[N], fa[N*2][H];
    struct interval{int l, r;}inter[N];
    bool cmp(interval a, interval b){return a.l < b.l;}
    int jump(int x, int k)
    {
        if(k < 0) return -1;
        for(int i = H-1; ~i; i--)
            if(k&(1<<i)) x = fa[x][i];
        return x;
    }
    void main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d%d",&l[i],&r[i]);
            arr[++arr_cnt] = l[i];
            arr[++arr_cnt] = r[i];
        }
        sort(arr+1, arr+1+arr_cnt);
        arr_cnt = unique(arr+1, arr+1+arr_cnt) - arr - 1;   
        int mx = 0;
        for(int i = 1; i <= n; i++)
        {
            l[i] = lower_bound(arr+1, arr+1+arr_cnt, l[i]) - arr;
            r[i] = lower_bound(arr+1, arr+1+arr_cnt, r[i]) - arr;
            mx = max(mx, max(l[i], r[i]));
        }
        for(int i = 1; i <= n; i++)
        {
            if(l[i] > r[i]) r[i] += mx;
            l[i+n] = l[i] + mx;
            r[i+n] = r[i] + mx;
            if(r[i+n] > 2*mx) r[i+n] = 2*mx;
            inter[i] = (interval){l[i], r[i]};
            inter[i+n] = (interval){l[i+n], r[i+n]};
        }
        sort(inter+1, inter+1+2*n, cmp);
        int far = 0;
        for(int i = 1, pos = 1; i <= mx*2; i++)
        {
            for(; pos <= 2*n && inter[pos].l <= i; pos++)far = max(far, inter[pos].r);
            fa[i][0] = far;
        }
        for(int i = mx*2; i; i--)
            for(int j = 1; j < H; j++)
                fa[i][j] = fa[fa[i][j-1]][j-1];
        int L = 0, R = n;
        for(; L < R; )
        {
            int mid = (L+R)>>1;
            if(jump(r[1], mid) >= l[1]+mx) R = mid;
            else L = mid + 1;
        }
        printf("%d",L + 1);
        for(int i = 2; i <= n; i++)
        {
            if(jump(r[i], L-1) >= l[i]+mx) printf(" %d",L);
            else if(jump(r[i], L) >= l[i]+mx) printf(" %d",L + 1);
            else printf(" %d",L + 2);
        }
    }
}
int main()
{
    runzhe2000::main();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值