CF - 496E Distributing Parts - 离线

problem:

给你n首歌,和每首歌的音调区间[a, b], a<=b

给你m个歌唱家,和每个歌唱家的音调区间[c, d]他最多可以唱k首歌, c<=d

一个歌唱家可以唱一首歌 iff c<=a<=b<=d

问能不能给出一种方案,使得每首歌都有歌唱家唱,能的话输出YES和每首歌由哪个歌唱家唱[1, m];不能输出NO

1<=n, m<=1e5

1<=a, b, c, d, k<=1e9


think:

1.对于a,b,c,d可以离散化,这样最后有2*n+2*m种音调。防止爆long long,k超多n的变成n

2.离线,按照音调左区间排序,当处理第i首歌的时候,把所有的cj<=ai的歌唱家都已经放进我们维护的数据结构,这样就不用考虑左区间了,只找右区间大于当前歌曲的歌唱家。

3.于是我们要维护每个右区间位置的歌唱家的信息。

4.如第一个样例,有5个音调值 1, 2, 3, 4, 5,开始的时候每个音调都是0,

1 4 2 => (0, 0, 0, 2, 0)

1 3    => (0, 0, 0, 1, 0) //这一步找到>=3的最小的不是0的位置,是4

2 5 1 => (0, 0, 0, 1, 1)

2 4    => (0, 0, 0, 0, 1)

3 5    => (0, 0, 0, 0, 0)

5.上一步明显可以随便维护,比如树状数组。但是这里有个问题。题目要求输出每首歌的歌唱家。所以用vector维护那些值分别是哪个歌唱家贡献的。


code:

const int N = 100111;
struct point{
    int a, b, c, d;
}p1[N], p2[N];
int n, m, cnt;
int li[N<<2];//用于离散化
LL C[N<<2];//树状数组
int ans[N];
vector<PII >vec[N<<2];

bool cmp(point x, point y){
    return x.a < y.a;
}

inline int low_bit(int x){
    return (x & (-x));
}

void update(int x, int v){
    for(; x <= cnt; x += low_bit(x)) C[x] += v;
}

LL ask(int x){
    LL ans = 0;
    for(; x > 0; x -= low_bit(x)) ans += C[x];
    return ans;
}

void add(int id){//加入歌唱家的信息
    update(p2[id].b, p2[id].c);
    vec[p2[id].b].PB(MP(p2[id].d, p2[id].c));
}

int mid_find(int fr){
    LL pre = ask(fr - 1);
    if(ask(cnt) - pre <= 0) return -1;
    int L = fr, R = cnt;
    while(L < R){
        int mid = (L + R) >> 1;
        LL val = ask(mid) - pre;
        if(val == 0) L = mid + 1;
        else {
            val = ask(mid - 1) - pre;
            if(val == 0) return mid;
            else R = mid - 1;
        }
    }
    return L;
}

int query(int id){
    int mi = mid_find(p1[id].b);
    if(mi == -1) return -1;
    update(mi, -1);
    PII tmp = vec[mi][vec[mi].size()-1];
    vec[mi].pop_back();
    ans[p1[id].c] = tmp.fi;
    tmp.se--;
    if(tmp.se > 0) vec[mi].PB(tmp);
}

int main(){
    while(scanf("%d", &n) != EOF){
        cnt = 0;
        for(int i = 1; i <= n; ++i){
            scanf("%d%d", &p1[i].a, &p1[i].b);
            p1[i].c = i;
            li[cnt++] = p1[i].a;
            li[cnt++] = p1[i].b;
        }
        scanf("%d", &m);
        for(int i = 1; i <= m; ++i){
            scanf("%d%d%d", &p2[i].a, &p2[i].b, &p2[i].c);
            if(p2[i].c > n) p2[i].c = n;
            p2[i].d = i;
            li[cnt++] = p2[i].a;
            li[cnt++] = p2[i].b;
        }
        sort(li, li +cnt);
        int j = 1;
        for(int i = 1; i < cnt; ++i){
            if(li[i] > li[i-1]) li[j++] = li[i];
        }
        cnt = j;
        for(int i = 0; i <= cnt; ++i) vec[i].clear();
        for(int i = 0; i <= cnt; ++i) C[i] = 0;
        for(int i = 1; i <= n; ++i){
            p1[i].a = lower_bound(li, li + cnt, p1[i].a) - li + 1;
            p1[i].b = lower_bound(li, li + cnt, p1[i].b) - li + 1;
        }
        for(int i = 1; i <= m; ++i){
            p2[i].a = lower_bound(li, li + cnt, p2[i].a) - li + 1;
            p2[i].b = lower_bound(li, li + cnt, p2[i].b) - li + 1;
        }
        sort(p1 + 1, p1 + n + 1, cmp);
        sort(p2 + 1, p2 + m + 1, cmp);
        bool has = 1;
        for(int i = 1, j = 0; i <= n; ++i){
            while(j + 1 <= m && p2[j+1].a <= p1[i].a){
                ++j;
                add(j);
            }
            int id = query(i);
            if(id == -1) {
                has = 0;
                break;
            }
        }
        if(has){
            puts("YES");
            for(int i = 1; i <= n; ++i) printf("%d ", ans[i]);
        } else {
            puts("NO");
        }
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值