Lexical Sign Sequence (线段树+贪心)

题目描述

Andi likes numbers and sequences, especially, sign sequences. A sign sequence is a sequence which consists of -1 and 1. Andi is a curious person, thus, he wants to build a sign sequence which length is N (the positions are numbered from 1 to N, inclusive).
However, Andi also likes some challenges. Therefore, he prefilled some positions in the sequence with -1 or 1 (the number in these positions cannot be changed). Andi also wants the sequence to fulfill K constraints.
For each constraint, there are 3 numbers: Ai, Bi, and Ci. This means that the sum of numbers which position is in the range [Ai,Bi] (inclusive) must be at least Ci.
Sounds confusing, right? It is not done yet. Since there can be many sequences that fulfill all the criteria above, Andi wants the sequence to be lexicographically smallest. Sequence X is lexicographically smaller than sequence Y if and only if there exists a position i where Xi < Yi and Xj = Yj for all j < i.
Find the sequence Andi wants.

 

输入

Input begins with a line containing two integers: N K (1≤N≤100000; 0≤K≤100000) representing the length of the sequence and the number of constraints, respectively. The second line contains N integers: Pi (-1≤Pi≤1). If Pi = 0, then the ith position in the sequence is not prefilled, otherwise the ith position in the sequence is prefilled by Pi. The next K lines, each contains three integers: Ai Bi Ci (1≤Ai≤Bi≤N;-N≤Ci≤N) representing the ith constraint.

 

输出

Output contains N integers (each separated by a single space) in a line representing the sequence that Andi wants if there exists such sequence, or “Impossible” (without quotes) otherwise.

 

样例输入 

3 2
0 0 0
1 2 2
2 3 -1

样例输出

1 1 -1

 

题意:让你用1和-1组成一个字典序最小的序列,满足题目给出的k+1个条件。

思路:如果对某一个位置有限制,就让他成为限制,其他的都赋值为-1,由于要求字典序最小,所以改动的-1越少越优,从后往前更新-1,这样贪心会让字典序最小,大区间包括小区间,所以修改小区间之后大区间就可能不用修改了,所以需要排序,先按右端点从小到大,再按左端点从小到大,然后对于区间的和,区间查询,如果和已经大于最低限制,那么continue,如果小于,就算一下需要更新几个-1,如果更新完当前区间所有的数之后,依然小于和的最低限制,那么输出-1,为了防止多余的遍历,把可以修改的点放进set里,对于此区间内的可修改的点二分查找,单点修改;

注意对于每个区间需要从右边开始修改,所以需要找到第一个小于等于右端点的点,然后单点更新,从set里删除,继续找,直到需要更新的-1的数量小于0,所以对于set来说,你可以存进去他们下标的相反数,这样查询之后取反,就是要的值

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 +10;
struct node{
    int x, y, sum;
}pos[maxn];
struct Tree{
    int l, r, sum;
}e[maxn << 2];
int a[maxn];
void build(int l, int r, int cur){
    e[cur].l = l;
    e[cur].r = r;
    e[cur].sum = 0;
    if(l == r) {
        e[cur].sum = a[l] == 0 ? -1 : a[l];
        return ;
    }
    int mid = l + r >> 1;
    build(l, mid, cur << 1);
    build(mid + 1, r, cur << 1 | 1);
    e[cur].sum = e[cur << 1].sum + e[cur << 1 | 1].sum;
}

void update(int l, int r, int cur, int pos){
    if(l == r) {
        e[cur].sum = 1;
        return ;
    }
    int mid = l + r >> 1;
    if(pos <= mid) update(l, mid, cur << 1, pos);
    else update(mid + 1, r, cur << 1 | 1, pos);
    e[cur].sum = e[cur << 1].sum + e[cur << 1 | 1].sum;
}
int query(int l, int r, int cur, int cl, int cr){
    if(cl <= l && r <= cr){
        return e[cur].sum;
    }
    int ans = 0;
    int mid = l + r >> 1;
    if(cl <= mid) ans += query(l, mid, cur << 1, cl, cr);
    if(cr >mid) ans += query(mid + 1, r, cur << 1 | 1, cl, cr);
    return ans;
}
bool cmp(node a, node b){
    if(a.y != b.y)
        return a.y < b.y;
    return a.x < b.x;
}
int ans[maxn];
int main(){
    int n, k, f = 0;
    set<int> s;
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        if(a[i] == 0) s.insert(-i);
    }
    for(int i = 0; i < k; i++) scanf("%d%d%d", &pos[i].x, &pos[i].y, &pos[i].sum);
    build(1, n, 1);
    sort(pos, pos + k, cmp);
    for(int i = 0; i < k; i++) {
        int cnt = query(1, n, 1, pos[i].x, pos[i].y);
        if(cnt >= pos[i].sum) continue;
        cnt = pos[i].sum - cnt + 1;
        cnt >>= 1;
        int x = -(*s.lower_bound(-pos[i].y));
        while(s.size() && cnt && x >= pos[i].x){
            update(1, n, 1, x);
            a[x] = 1;
            s.erase(-x);
            cnt--;
            x = -(*s.lower_bound(-pos[i].y));
        }
        if(cnt) {
            f = -1;
            break;
        }
    }
    if(f == -1) puts("Impossible");
    else {
        for(int i = 1; i <= n; i++) {
            if(a[i] == 0) a[i]--;
            printf("%d%c", a[i]," \n"[i == n]);
        }
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值