[SCOI2015] 国旗计划
  YdxfFsjpYokY 2023年11月02日 69 0


也许更好的阅读体验

[SCOI2015] 国旗计划_Code
给一个长度为[SCOI2015] 国旗计划_线段覆盖_02的环,上面有[SCOI2015] 国旗计划_acm_03条线段,问在第[SCOI2015] 国旗计划_倍增_04条线段必须被选中的条件下覆盖整个环最少需要多少线段,不存在线段覆盖线段的情况,注意是线段,[SCOI2015] 国旗计划_oi_05[SCOI2015] 国旗计划_线段覆盖_06之间少了[SCOI2015] 国旗计划_oi_07这一段
[SCOI2015] 国旗计划_acm_08

[SCOI2015] 国旗计划_acm_09
用倍增,预处理出一个类似[SCOI2015] 国旗计划_线段覆盖_10表的表出来
环的问题先倍长数组,然后将线段也双倍
将线段按照左端点大小排序,由于不存在线段覆盖情况,因此可以肯定没有两条线段有端点相等的情况,并且左端点更大的线段右端点也更大
[SCOI2015] 国旗计划_线段覆盖_11表示排序后第[SCOI2015] 国旗计划_倍增_04条线段开始往后挑选[SCOI2015] 国旗计划_acm_13条线段并且使得覆盖的长度最长,最后一条线段的序号
考虑状态转移时并不是直接等于[SCOI2015] 国旗计划_oi_14,因为最后一条线段是已经被选了的,因此应该从下一条线段开始
这里的下一条线段并不是序号+1,而应该是左端点在最后一条线段右端点范围内的尽可能大的那一条
这是很明显的贪心,如果有多条线段可以和当前覆盖范围拼起来,当然选能够覆盖的更多的线段
我们可以预处理出[SCOI2015] 国旗计划_线段覆盖_15,表示第[SCOI2015] 国旗计划_倍增_04条线段的下一条线段是哪一个,因为从左到右[SCOI2015] 国旗计划_线段覆盖_15只会越来越大,因此[SCOI2015] 国旗计划_oi_18就可以处理出来
转移方程就变成了[SCOI2015] 国旗计划_Code_19
查询时,我们用类似求[SCOI2015] 国旗计划_acm_20那样的方法不断接近覆盖整个环但始终不覆盖,最后就能得出还差一条线段就可以覆盖环的结果

[SCOI2015] 国旗计划_Code_21

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 400005;
const int lim = 21;
struct seg{
    int l, r, id;
}s[maxn];
int n, m;
int lg[maxn], mi[maxn], ans[maxn], nxt[maxn];
int f[maxn][lim];
bool cmp (seg x, seg y)
{
    return x.l < y.l;
}
void deal ()
{
    mi[0] = 1;
    for (int i = 1; i < lim; ++i)  mi[i] = mi[i - 1] << 1;
    for (int i = 1; i <= n; ++i)    f[i][0] = i;
    for (int p = 2, i = 1; i <= n; ++i) {
        while (p < n && s[p + 1].l <= s[i].r)    ++p;
        nxt[i] = p;
    }
    for (int i = 1; i <= lg[n]; ++i)
        for (int j = 1; j <= n; ++j) {
            if (n - j + 1 >= mi[i]) f[j][i] = f[nxt[f[j][i - 1]]][i - 1];//只需求前面n条线段的答案,因此第2n条线段是不可能被用上的,若f[j][i]被赋值为0就说明不需要用这么多线段就可以覆盖环了
            else break;
        }
}
int query (int x)
{
    int res = 0, lt = s[x].l;
    for (int i = lg[n - x + 1]; ~i; --i)
        if (f[x][i] && s[f[x][i]].r - lt + 1 <= m)//f[x][i]为0说明不需要跳这么远
            res += mi[i], x = nxt[f[x][i]];
    return res + 1;
}
int main ()
{
    scanf("%d%d", &n, &m);
    lg[0] = -1;
    for (int i = 1; i <= n; ++i) {
		scanf("%d%d", &s[i].l, &s[i].r);
        if (s[i].r < s[i].l)    s[i].r += m;
	}
    for (int i = 1; i <= n; ++i) {
        s[i + n].l = s[i].l + m;
        s[i + n].r = s[i].r + m;
	}
    n <<= 1;
    for (int i = 1; i <= n; ++i)    s[i].id = i, lg[i] = lg[i >> 1] + 1;
    sort(s + 1, s + n + 1, cmp);
    deal();
    for (int i = 1; i <= n / 2; ++i)
        ans[s[i].id] = query(i);
    n >>= 1;
    for (int i = 1; i <= n; ++i)    printf("%d ", ans[i]);
    return 0;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  cB14ff7Kmzpi   2023年12月19日   32   0   0 iosiosgogoCodeCode
YdxfFsjpYokY