首页 > 其他分享 >洛谷题单指南-分治与倍增-P4155 [SCOI2015] 国旗计划

洛谷题单指南-分治与倍增-P4155 [SCOI2015] 国旗计划

时间:2024-09-26 19:47:42浏览次数:8  
标签:个点 start int 洛谷题 go P4155 区间 SCOI2015 倍增

原题链接:https://www.luogu.com.cn/problem/P4155

题意解读:在m个点的环上,有n个区间,且各个区间之间没有包含关系,计算从每个区间开始,最少要多少个区间能覆盖环上所有m个点。

解题思路:

本质上是一个区间覆盖问题!

1、破环成链

由于题目中是一个环,对于环的问题,在区间DP中介绍过,经典处理是破环成链,并且延展两倍长度。

2、区间覆盖

从某一个区间开始,要用最少的区间覆盖长度m的点,可以用经典的区间覆盖贪心算法:

先将区间按左端点排序,对于一个区间i,l[i]~r[i],下一个区间j,必须满足l[j] < r[i]并且r[j]经可能远。

由于题目说明“每名边防战士的奔袭区间都不会被其他边防战士的奔袭区间所包含”,即各个区间之间没有包含关系,

可以认为区间的左端点越大则右端点也越大,因此对于区间i的下一个区间j,就是满足l[j] <= r[i]的最后一个区间。

如果对于每一个区间,都执行一遍区间覆盖贪心算法,统计区间数,总体复杂度为O(n^2),会超时!

3、倍增预处理

借助于倍增ST表思想,可以将从每个区间开始跳2^k个区间之后到哪个区间进行初始化,即从某个士兵传递2^k次国旗后到哪个士兵

设go[i][j]表示从第i个士兵开始,传2^j次国旗后到哪个士兵

借助于区间覆盖算法,可以初始化go[i][0]为从i区间跳1次后到的区间,然后通过递推可计算go[i][j] = go[go[i][j - 1]][j - 1]

4、倍增计算

对于从start区间开始,最少经过多少个区间能覆盖m个点,可以借助倍增思想

从可以跳的最多次数x开始递减,找到go[start][x]区间右端点距离起点区间左端点正好少于m个点的x,start更新为go[start][x],累加x,直到超过m个点,记录从该起点开始的答案。

5、记录答案

由于将士兵(区间)进行了排序,要保留原来的序号,记录序号对应的答案。

最后输出所有答案即可,总体复杂度是O(n * logn)

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 200005;

struct node
{
    int id, l, r;
} a[2 * N];

int go[2 * N][20]; //go[i][j]表示从第i个士兵开始,传2^j次国旗后到哪个士兵
int n, m;
int ans[N];

bool cmp(node &a, node &b)
{
    return a.l < b.l;
}

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
    {
        a[i].id = i;
        scanf("%d%d", &a[i].l, &a[i].r);
        if(a[i].r < a[i].l) 
        {
            a[i].r += m; //对于右端点比左端点小的情况,要加上m
        }
    } 
    sort(a + 1, a + n + 1, cmp);
    //破环成链,复制两倍长度
    for(int i = 1; i <= n; i++)
    {
        a[i + n].l = a[i].l + m;
        a[i + n].r = a[i].r + m;
    }
    //初始化go
    for(int i = 1, j = i; i <= 2 * n; i++) //这里双指针i,j的使用很关键,不能在下面每次都定义j=i,这样就是O(n*n)了,会超时
    {
        while(j <= 2 * n && a[j].l <= a[i].r) j++;
        go[i][0] = j - 1; //用区间覆盖算法找到i的下一个区间
    }
    for(int j = 1; j < 20; j++)
    {
        for(int i = 1; i + (1 << j) - 1 <= 2 * n; i++)
        {
            go[i][j] = go[go[i][j - 1]][j - 1];
        }
    }
    //倍增计算
    for(int start = 1; start <= n; start++)
    {
        int t = start;
        int cnt = 1;
        for(int x = 19; x >= 0; x--)
        {
            int end = go[t][x]; //从t跳2^x次到end
            if(end != 0 && a[end].r - a[start].l + 1 <= m) 
            {
                cnt += 1 << x;
                t = end;
            }
        }
        ans[a[start].id] = cnt + 1; //跳了cnt次覆盖了不超过m个点,还要跳一次才能覆盖第m个点到第1个点之间的空隙
    }
    //输出结果
    for(int i = 1; i <= n; i++) printf("%d ", ans[i]);

    return 0;
}

 

标签:个点,start,int,洛谷题,go,P4155,区间,SCOI2015,倍增
From: https://www.cnblogs.com/jcwy/p/18433729

相关文章

  • 洛谷题单指南-分治与倍增-P3517 [POI2011] WYK-Plot
    原题链接:https://www.luogu.com.cn/problem/P3517题意解读:有n个连续的点p1,p2,...,pn,将这n个点分成不超过m堆,每堆点连续,每一堆都缩成一个点qi,要使得原来的点p1~ps距离qi的最大值最小(最相似),求这个相似度,并计算一共分成几堆,以及每堆缩到的点qi的坐标。解题思路:要使得若干点缩到一......
  • 洛谷题单指南-分治与倍增-P3509 [POI2010] ZAB-Frog
    原题链接:https://www.luogu.com.cn/problem/P3509题意解读:n个点,每个点上有一只青蛙每次跳到距离自己第k近的点,m次之后跳到哪个点。解题思路:1、计算距离每个点第k近的点,存入ne[N]给定一个点i,距离i第k近的点一定在长度为k+1个点的窗口内,窗口包括i并且,第k近的点只能是左端点或者......
  • 洛谷题单指南-分治与倍增-P2345 [USACO04OPEN] MooFest G
    原题链接:https://www.luogu.com.cn/problem/P2345题意解读:有n头牛,每头牛都有听力v、坐标x两个属性,要计算每两头牛的max{vi​,vj​}×∣xi​−xj​∣之和。解题思路:首先想到的肯定是枚举法,需要O(n^2)的复杂度有没有优化的方法?可以采用分治法!由于是计算两头牛之间的max{vi​,......
  • 洛谷题单指南-分治与倍增-P2415 集合求和
    原题链接:https://www.luogu.com.cn/problem/P2415题意解读:计算集合所有子集中元素之和。解题思路:集合的特性:互异性,元素各不相同来看样例:23,可以组成的子集有空23232和3都出现2次再比如:123,可以组成的子集有空12312 13231231,2,3各出现4次由于在集合中......
  • 洛谷题单指南-分治与倍增-P7167 [eJOI2020 Day1] Fountain
    原题链接:https://www.luogu.com.cn/problem/P7167题意解读:从喷泉任意一个圆盘倒水,水流经的圆盘直径必须递增,水最后流到哪个圆盘。解题思路:1、枚举法有30%的数据范围在N<=1000,Q<=1000,因此枚举也可以得到30分。可以通过单调栈预计算每个圆盘后面第一个直径更大的圆盘位置Next[......
  • 洛谷题单指南-分治与倍增-P1226 【模板】快速幂
    原题链接:https://www.luogu.com.cn/problem/P1226题意解读:快速幂模版题。解题思路:1、分治法要计算a^b,可以对b分情况讨论:如果b是偶数,即b=2t,a^b=a^t*a^t如果b是奇数,即b=2t+1,a^b=a*a^t*a^t很容易用递归实现100分代码:#include<bits/stdc++.h>usingnamespa......
  • 洛谷题单指南-分治与倍增-P1966 [NOIP2013 提高组] 火柴排队
    原题链接:https://www.luogu.com.cn/problem/P1966题意解读:计算两个序列∑(ai​−bi​)^2的最小值,对10^8-3取模。解题思路:1、贪心思路要使得两个序列对应位置元素之差的平方和最小,必须满足两个序列相对排序是一致的,什么意思?设a序列有两个元素:a1,a2,b序列有两个元素b1,b2当a1<a2,b......
  • 洛谷题单指南-分治与倍增-P1908 逆序对
    原题链接:https://www.luogu.com.cn/problem/P1908题意解读:求序列逆序对数。解题思路:1、暴力法对于每一个数,寻找后面有多少数比其小,或者采用冒泡排序,交换的次数即逆序对的个数,复杂度为O(n^2)2、归并排序法在归并排序过程中,会进行有序序列的合并,设两部分连续的有序序列为a[s1,......
  • 洛谷题单指南-分治与倍增-P1177 【模板】归并排序
    原题链接:https://www.luogu.com.cn/problem/P1177题意解读:归并排序模版题。解题思路:第一步:确定分界点。mid=(l+r)/2第二步:排序。对左右两边递归排序第三步:归并。合并左右两边排序好的内容关键在第三步:通过双指针对两个有序数组进行合并100分代码:#include<bits/std......
  • 洛谷题单指南-常见优化技巧-P1714 切蛋糕
    原题链接:https://www.luogu.com.cn/problem/P1714题意解读:求长度不超过m的最大子段和解题思路:1、暴力法设a[N]表示原数组,s[N]是a[N]的前缀和,对于每一个元素s[i],计算其与前m个元素之差,取差值最大值,用代码表示:for(inti=1;i<=n;i++){for(intj=i-1;j>=i-m......