1167E (尺取法)

http://codeforces.com/problemset/problem/1167/E

题意:

删除一个区间f(l, r)后使剩下的数不递减, 求这样区间的个数

因为一个数可以在这个数组中出现多次,所以转化为,先预处理出所有数的最左边的下标和最右边的下标,这样每个数都有一个区间

然后求最多有多少个f(l, r)使剩下的数的左右区间不相交

解析:

先从后向前求出一个递减的序列,如果当前数r的区间扰乱了这个递减的序列,那么f(1,r)、 f(1, r + 1)······f(1, x)都符合要求 所以就有x - r + 1个

然后我们定r,缩l判断f(2, r)是否符合要求,如果不符合则r向后累加

当然首先要保证1的区间和2的区间,是递增的 如果不递增,输出结果即可,自己想一想为什么

 

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1001000, INF = 0x7fffffff;
typedef long long LL;
LL l[maxn], r[maxn];
LL vis[maxn];

struct node
{
    LL succ;
}Node[maxn];


int main()
{
    LL n, x;
    cin >> n >> x;
    for(int i = 1; i <= x; i++) l[i] = INF;

    for(LL i = 1; i <= n; i++)
    {
        LL tmp;
        cin >> tmp;
        Node[tmp].succ = tmp;
        vis[tmp] = 1;
        l[tmp] = min(l[tmp], i);
        r[tmp] = i;
    }
    LL pre, succ;
    for(int i = 1; i <= x; i++)
    {
        if(vis[i] == 0)
        {
            if(i == 1) r[i] = 1, succ = i;
            else r[i] = pre, Node[i].succ = succ;
        }
        if(vis[i]) succ = i;
        pre = r[i];
    }

    for(int i = x; i >= 1; i--)
    {
        if(vis[i] == 0)
        {
            if(i == x) l[i] = n;
            else l[i] = pre;
        }
        pre = l[i];
    }
    int pos = -1;
    for(int i = x; i >= 2; i--)
    {
        if(r[i - 1] > l[i])
        {
            pos = Node[i - 1].succ;
            break;
        }
    }
    if(pos == -1)
    {
        LL ret = (LL)(x * (x + 1) / 2);
        printf("%lld
", ret);
        return 0;

    }
    LL res = 0;
    res += x - pos + 1;
  //  cout << res << endl;
    for(int i = 1; i <= x; i++)
    {
        if(l[i] < r[i - 1] && vis[i] != 0) break;

        while(pos <= x - 1 && r[i] > l[pos + 1]) pos++;
        res += x - pos + 1;

    }
    cout << res << endl;


    return 0;
}

 

原文地址:https://www.cnblogs.com/WTSRUVF/p/10912013.html