洛谷 P3586 [POI2015]LOG

题目描述

维护一个长度为n的序列,一开始都是0,支持以下两种操作:1.U k a 将序列中第k个数修改为a。2.Z c s 在这个序列上,每次选出c个正数,并将它们都减去1,询问能否进行s次操作。每次询问独立,即每次询问不会对序列进行修改。

输入输出格式

输入格式:

第一行包含两个正整数n,m(1<=n,m<=1000000),分别表示序列长度和操作次数。接下来m行为m个操作,其中1<=k,c<=n,0<=a<=10^9,1<=s<=10^9。

 

输出格式:

包含若干行,对于每个Z询问,若可行,输出TAK,否则输出NIE。

 

输入输出样例

输入样例#1: 复制
3 8
U 1 5
U 2 7
Z 2 6
U 3 1
Z 2 6
U 2 2
Z 2 6
Z 2 1
输出样例#1: 复制
NIE
TAK
NIE
TAK

说明

维护一个长度为n的序列,一开始都是0,支持以下两种操作:

1.U k a 将序列中第k个数修改为a。

2.Z c s 在这个序列上,每次选出c个正数,并将它们都减去1,询问能否进行s次操作。

每次询问独立,即每次询问不会对序列进行修改。

 

这道题就是完全树状数组嘛 

一开始看到这个玩意儿觉得就是建造一颗值域树状数组  然后再查询值大于等于$s$的个数

如果个数满足大于等于$c$ 那这个玩意儿就是合法的

但是这个时候也出现了问题 有可能我大于等于$s$的个数不大于$c$ 但是他仍然合法

比如$2 2 3 6 9$ 我要取出两个数 进行$9$次操作 按照之前的想法这个是不行的 

但是在这种情况下它可以用多个不同的小数来补齐 那么什么时候才能算是合法的呢

考虑如果大于等于$s$的个数小于$c$该怎么办 假设大于等于$s$数的个数有$k$个

那么这个时候除去那$k$个数 剩余我们还需要减去$(c - k) * s$次 就用小于$s$的数的和与这个做比较

如果大于等于那这个就是合法的 所以还需要维护一颗求数字之和的树状数组

仔细想一下 这个为什么不会出现小数不够减的情况呢

因为有先决条件$sum < (c - k) * s$ 又因为不合法的数均是小于$s$ 所以不合法的数的个数一定大于$(c - k)$

所以我们一定能够选出$(c - k)$ 个数 然后用后面的小数补齐 使它一定够减 就像这样

一定能够补齐 然后就各种操作搞一搞 要离散化

代码

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

typedef long long ll;
const int N = 2 * 1e6 + 5;
int n,m,tot,t[N],b[N];
ll c[N],v[N];
struct option {
    
    int pd,x,y;
}q[N >> 1],a[N >> 1];

void Init( ) {
    
    scanf("%d%d",& n,& m);
    for(int i = 1;i <= m;i ++) {
        char opt[5];
        ll a,k;
        scanf("%s",opt);
        if(opt[0] == 'U') {
            scanf("%d%d",& a,& k);
            q[i].x = a; q[i].y = k;
            q[i].pd = 1;
            b[++ tot] = k;
        }
        else {
            scanf("%d%d",& a,& k);
            q[i].x = a,q[i].y = k;
            q[i].pd = 0;
        }
    }
    sort(b + 1,b + tot + 1);
    tot = unique(b + 1,b + tot + 1) - b - 1;
}

int lowbit(int a) {
    
    return a & (-a);
}

void modify(ll *c, int pos, ll del) {
    
    while(pos <= tot) {
        c[pos] += del;
        pos += lowbit(pos);
    }
}

ll query(ll *c,int pos) {
    
    ll ans = 0;
    while(pos >= 1) {
        ans += c[pos];
        pos -= lowbit(pos);
    }
    return ans;
}

void Solve( ) {
    
    for(int i = 1;i <= m;i ++) {
        if(q[i].pd) {
            int pos1 = lower_bound(b + 1, b + tot + 1, t[q[i].x]) - b;
            int num = query(c, pos1) - query(c, pos1 - 1);
            if(num) modify(c, pos1, -1);
            if(num) modify(v, pos1, -t[q[i].x]);
            int pos2 = lower_bound(b + 1, b + tot + 1, q[i].y) - b;
            modify(c, pos2, 1);
            modify(v, pos2, q[i].y);
            t[q[i].x] = q[i].y;
        }
        else {
            int pos = lower_bound(b + 1, b + tot + 1, q[i].y) - b;
            int p = query(c, tot) - query(c, pos - 1);
            ll asum = query(v, tot),bsum = query(v, pos - 1);
            ll num = asum - bsum;
            if(p >= q[i].x) {
                printf("TAK\n"); continue;
            }
            else if(bsum >= 1ll * (q[i].x - p) * q[i].y) {
                printf("TAK\n"); continue;
            }
            else printf("NIE\n"); continue;
        }
    }
}

int main( ) {
    
    Init( );
    Solve( );
}
posted @ 2018-09-17 09:50  阿澈说他也想好好学习  阅读(245)  评论(0编辑  收藏  举报