操作1是对序列的修改,重点来看如何实现操作2
维护一个长度为 n 的序列,一开始都是 0,支持以下两种操作:
U k a
将序列中第 k 个数修改为 a。
Z c s
在这个序列上,每次选出 c 个正数,并将它们都减去 1,询问能否进行 s 次操作。每次询问独立,即每次询问不会对序列进行修改。
“减去1”的操作如何直观得被感受呢,可以把一个数写成很多个1累积而成的形式
例如,现在有一组数 1 3 2 4 3,可以表示成下面这样
4 | ||||
2 | 4 | 5 | ||
2 | 3 | 4 | 5 | |
1 | 2 | 3 | 4 | 5 |
1 | 3 | 2 | 4 | 3 |
1是第一个数,所以其上为1,之后类推
每次操作取c个数,取s次,这样看取数很乱,为了看得清楚一些,把每次取出来的c个数放在一排,一共放s排
例如,取4个数,取3次,以上表格通过移动格子里的数可以变成如下情况:
4 | ||||
第1次取 | 2 | 1 | 4 | 5 |
第2次取 | 2 | 3 | 4 | 5 |
第3次取 | 2 | 3 | 4 | 5 |
1 | 3 | 2 | 4 | 3 |
此时,每次取的4个数没有出现重复的,并且我们的数据可以满足取3次数
右上角的4没有用到,原因是一共取3次,第4个数出现了4次,4次比3次大,就算每次都取4,也只能取3个,不会取完
所以如果一个数是n,取s次的话,若n>s,他最多在这个c*s的表格中贡献的格子数量最多就是s
如果n<=s,在这个表格中贡献的格子数量最多是n
如果所有数能贡献的格子数量的最大加和>=c*s,是不是就一定能构成这样的c*s的网格,从而满足题意呢?
如果能说明通过移动格子,能够保证每次取数不会取到两个或多个同样的数,就可以了
从简单情况开始:要取c个数,取s次,如果现在只有c个数,那必须要每个数都大于等于s,才能满足题目条件,此时每次取这不同的c个数,一定是没有重复的
进一步,如果数的个数超过了c,但是贡献的格子数量的最大值大于等于了c*s,说明在简单情况的c*s网格中,每一行的c格中,有一些格子被替换成了原本的c种数以外的数,替换后,与同行相比,因为是c种数以外的数,所以不会有与同行中的数种类相同的数,所以不会出现重复
现在问题转化为,每一个数有一个贡献值,这个贡献值是数的值,但最大是s,如果所有数贡献值的和加起来(此时得到的是最大贡献值),大于等于了c*s,就可以满足取数要求
以上面的情况举例:
4 | ||||
2 | 4 | 5 | ||
2 | 3 | 4 | 5 | |
1 | 2 | 3 | 4 | 5 |
1 | 3 | 2 | 4 | 3 |
取c=4,s=3,每个数的最大贡献值依次是:1 3 2 3 3,和为12,12>=c*s,所以可以满足取数要求
现在,问题转化为,给定s,求数列中所有数的最大贡献值的和
直观来讲,使用树状数组,用tree[s]表示所有数小于等于s的部分的和,但是这样对于每个数n
需要将 i : 1~n , tree[i]+=i
i n+1~最大的s tree[i]+=n
从1到最大的s,即使采用了离散化,也有1e6的数量级,如果有1e6数量的操作,就变为1e12的数量级超过1e8,会超时
求最大贡献值的和有没有其他方法呢?
可以用所有数的总和减去超出s部分的和
例如上例,所有数的总和是13,超出s的部分有一个1,13-1=12
数的总和好解决,每次修改数的时候对和修改
但是,超出s的部分的和怎么求?
不能又是每个数对s进行分类讨论,不然是同样的复杂度
详细来说,就是,如果一个数是5,tree[1]+=4,tree[2]+=3 ....
可以用大于等于s的数的总和,减去(s*大于等于s的数的数目)
大于等于s的数的总和好解决,大于等于s的数的数目也好解决,两者都使用树状数组正常更新
要使用离散化减少内存开销
#include
#include
#include
using namespace std;
#define lowbit(x) (x&(-x))
#define ll long long
const int N=1e6,maxn=1e6+5;
ll a[maxn],b[maxn],n,m;
ll tree1[maxn],tree2[maxn],old[maxn];
struct TempType{
ll id,v;
bool operator < (const TempType& rhs) const {
return v>n>>m;
for(int i=1;i<=m;i++){
cin>>opt[i].op>>opt[i].x>>opt[i].y;
dat[i].id=i;
dat[i].v=opt[i].y;
}
//离散化
stable_sort(dat+1,dat+1+m);
dat[0].v=-1;
dat[0].id=0;
for(int i=1;i<=m;i++){
if(dat[i].v==dat[i-1].v){
b[dat[i].id]=b[dat[i-1].id];
}else{
b[dat[i].id]=b[dat[i-1].id]+1;
}
}
/*离散化测试
for(int i=1;i<=m;i++){
cout<=x*y){
cout<<"TAK"<<"\n";
}else{
cout<<"NIE"<<"\n";
}
}
}
return 0;
}