2811: [Apio2012]Guard

花了一天时间颓废这道题(颓废才是重点。。。)
贪心的想,从左望右,对于可以放的位置,放在最后一定能得到最优解,反之亦然
那么先考虑处理掉所有0的区间,然后离散化。
然后处理出对于每个区间最少花多少代价处理出来。(前缀 ,后缀)
那么容易想到,如果离散化后只有k个点,那么直接输出答案即可。
并且 如果区间大小为1 也必须直接输出。
除此之外,所有输出的点一定是当前区间的右端点。
那么考虑不放在右端点此时的最优解一定是右端点的左边第一个点,
那么根据之前预处理出来的东西,可以判断出此时需要最少多少点,搞定
复杂度 O(mlogm) O ( m log ⁡ m )

c++`代码如下:

#define lowbit(x) (x&-x)
#define rep(i,x,y) for(register int i = x ; i <= y; ++ i)
#define repd(i,x,y) for(register int i = x ; i >= y; -- i)
using namespace std;
typedef long long ll;
template<typename T>inline void read(T&x)
{
    char c;int sign = 1; x = 0;
    do { c = getchar(); if(c == '-') sign = -1; }while(!isdigit(c));
    do { x = x * 10 + c - '0'; c = getchar(); }while(isdigit(c));
    x *= sign;
}

const int N = 1e5+50;
int n,m,k;
int t[N];

int w[N],z[N],s1[N],s2[N],cnt,tot,top;
struct DATA { int l,r; }a[N],b[N];
const bool cmp(DATA a,DATA b) { return a.l < b.l || a.l == b.l && a.r > b.r; }

int low[N],upp[N];

int main()
{
    read(n); read(k); read(m);
    rep(i,1,m)
    {
        int l,r,op;
        read(l); read(r); read(op);
        if(op == 1)
        {
            b[++ tot].l = l;
            b[tot].r = r;
        }
        else t[l] -= 1,t[r + 1] += 1;
    }

    rep(i,1,n) t[i] += t[i - 1];
    rep(i,1,n) if(! t[i] )  w[++cnt] = i;

    if(k == cnt)
    {
        rep(i,1,cnt)
            printf("%d\n",w[i]);
        return 0;
    }

    w[cnt + 1] = n + 1;
    rep(i,1,cnt + 1)
    {   
        rep(j,w[i],w[i+1]-1)    
            low[j] = i;
        rep(j,w[i-1]+1,w[i])    
            upp[j] = i;
    }

    rep(i,1,tot)
    {
        b[i].l = upp[b[i].l];
        b[i].r = low[b[i].r];
        if(b[i].l == b[i].r) z[b[i].l] = 1;
    }

    sort(b + 1 ,b + 1 + tot,cmp);

    rep(i,1,tot)
    {
        if(a[top].l > a[top].r ) continue;
        while(top && a[top].r >= b[i].r) -- top;
        a[++top] = b[i];
    }

    int cur = 0;

    m = top;
    rep(i,1,m)
        if(a[i].l <= cur) s1[i] = s1[i - 1];
        else s1[i] = s1[i - 1] + 1,cur = a[i].r;

    cur = n + 1;
    repd(i,m,1)
        if(a[i].r >= cur) s2[i] = s2[i + 1];
        else s2[i] = s2[i + 1] + 1,cur = a[i].l; 

    rep(i,a[m].l+1,cnt) z[i] = cnt + 1;
    repd(i,m,1)
        rep(j,a[i-1].l + 1,a[i].l)
            z[j] = i;

    bool flag = 0;
    rep(i,1,m)
    {
        if(s1[i] == s1[i - 1]) continue;
        if(a[i].l == a[i].r)
        {
            flag = 1;
            printf("%d\n", w[a[i].l ]);
            continue;
        }

        if(s1[i - 1] + 1 + s2[z[a[i].r]]> k)
        {
            flag = 1;
            printf("%d\n",w[a[i].r]);
        }
    }

    if(!flag) puts("-1");

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值