Luogu 4155 [SCOI2015]国旗计划

BZOJ 4444

倍增 + 贪心。

发现是一个环,先按照套路把环断开复制一倍,这样子的话覆盖完整个环就相当于覆盖一条长度不小于$m$的链,注意这样子有一些区间在新的这条链上会出现两次。

我们为了找到最小的满足要求的答案,在选择完一个区间$[l, r]$之后会选择左端点不超过$r$但是右端点尽量大的区间,因为题目保证了所有的区间不相互包含,这样子的话我们只要找到左端点最靠右的区间就可以了。

用$f_{i, j}$表示$i$号区间向下跳$2^j$个区间能跳到的最后一个区间,接下来只要按照套路倍增一下就好了。

注意弄一个最大的右端点(要足够大,我就是这样WA了两次……),使倍增的时候能停下来。

时间复杂度$O(nlogn)$。

实现的时候需要注意细节。

#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N = 4e5 + 5;
const int Lg = 22;
const int inf = INT_MAX;

int n, m, tot = 0, ans[N], f[N][Lg];

struct Seg {
    int l, r, id;
    
    friend bool operator < (const Seg &x, const Seg &y) {
        if(x.l != y.l) return x.l < y.l;
        else return x.r < y.r;
    }
        
} a[N];

template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48; 
    X *= op;
}

inline int solve(int x) {
    int res = 2, pos = x;
    for(int i = 20; i >= 0; i--)
        if(f[pos][i] && a[f[pos][i]].r < a[x].l + m)
            pos = f[pos][i], res += (1 << i);
    return res;
}

int main() {
//    freopen("3.in", "r", stdin);
//    freopen("my.out", "w", stdout);
    
    read(n), read(m);
    for(int l, r, i = 1; i <= n; i++) {
        read(l), read(r);
        if(l > r) {
            r += m;
            a[++tot].l = l, a[tot].r = r, a[tot].id = i;
        } else {
            a[++tot].l = l, a[tot].r = r, a[tot].id = i;
            a[++tot].l = l + m, a[tot].r = r + m, a[tot].id = i;
        }
    }
    
    sort(a + 1, a + 1 + tot);
    a[tot + 1].r = inf;
    
    for(int j = 1, i = 1; i <= tot; i++) {
        for(; j <= tot && a[j + 1].l <= a[i].r; ++j);
        f[i][0] = j;
    }
    for(int j = 1; j <= 20; j++)
        for(int i = 1; i <= tot; i++)
            f[i][j] = f[f[i][j - 1]][j - 1];
    
    for(int i = 1; i <= tot; i++) {
        if(a[i].l > m) continue;
        ans[a[i].id] = solve(i);
    }
    
    for(int i = 1; i <= n; i++)
        printf("%d%c", ans[i], i == n ? '\n' : ' ');
//    printf("\n");
    
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/CzxingcHen/p/10108354.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值