洛谷 P3586 [POI2015]LOG
维护一个长度为n的序列,一开始都是0,支持以下两种操作:1.U k a 将序列中第k个数修改为a。2.Z c s 在这个序列上,每次选出c个正数,并将它们都减去1,询问能否进行s次操作。每次询问独立,即每次询问不会对序列进行修改。
这个题看起来很nb的样子
首先我们可以把询问转化一下
既然要进行s次,那么那些大于等于s是一定要选的,假设这些数有cnt个
再考虑比s小的,假设这些数的和为sum,有x个
那么我们要满足\(sum\ge (c-cnt)\times s\)这个条件
看起来很正确但又感觉少了点什么QAQ,我们来证明一下
因为和已经满足条件了,那么我们只需要知道每次选的数是不是能有c个就可以,也就是\(x\ge c-cnt\)
考虑c最少的情况,也就是\(sum=\sum s-1\)
那么\(x=\frac{sum}{s-1}\),所以\(\frac{sum}{s-1}\ge c-cnt\)
移项得到\(sum\ge (c-cnt)\times (s-1)\)
而根据原式\(sum\ge (c-cnt)\times s\),上面这个结论就已经成立啦QAQ
所以我们只需要维护比一个数大的数有多少个,比一个数小的数的值的和就可以了
对于这个题而言,我们可以选择离散化+树状数组
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define LL long long
const int N = 1e6;
using namespace std;
struct node
{
int opt,c,s;
}q[N + 5];
int n,m,a[N + 5],data[N + 5],c[N + 5],mp[N + 5],na[N + 5];
LL s[N + 5];
char ch;
int lowbit(int x)
{
return x & (-x);
}
void addc(int k,int x)
{
if (k == 0)
return;
for (;k <= m;k += lowbit(k))
c[k] += x;
}
void adds(int k,int x)
{
if (k == 0)
return;
for (;k <= m;k += lowbit(k))
s[k] += x;
}
int queryc(int k)
{
int ans = 0;
for (;k;k -= lowbit(k))
ans += c[k];
return ans;
}
LL querys(int k)
{
LL ans = 0;
for (;k;k -= lowbit(k))
ans += s[k];
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i = 1;i <= m;i++)
{
cin>>ch;
scanf("%d%d",&q[i].c,&q[i].s);
data[i] = q[i].s;
if (ch == 'U')
q[i].opt = 1;
else
q[i].opt = 2;
}
sort(data + 1,data + m + 1);
for (int i = 1;i <= m;i++)
mp[i] = lower_bound(data + 1,data + m + 1,q[i].s) - data,na[mp[i]] = q[i].s;
for (int i = 1;i <= m;i++)
{
if (q[i].opt == 1)
{
addc(a[q[i].c],-1);
adds(a[q[i].c],-na[a[q[i].c]]);
a[q[i].c] = mp[i];
addc(a[q[i].c],1);
adds(a[q[i].c],q[i].s);
}
else
{
int cnt = queryc(m) - queryc(mp[i] - 1);
if (querys(mp[i] - 1) >= 1ll * q[i].s * (q[i].c - cnt))
printf("TAK\n");
else
printf("NIE\n");
}
}
return 0;
}