洛谷P3586 [POI 2015] LOG Logistyka 分析与解答

操作1是对序列的修改,重点来看如何实现操作2

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

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

  2. 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;
}

你可能感兴趣的:(算法,c++)