[loj6254]最优卡组

特殊处理$c_{i}=1$的$i$,显然对这些$a_{i,1}$求和即可,以下都假设$c_{i}ge 2$

对于每一个$i$,将$a_{i,j}$从大到小排序;接下来,对于所有$i$,按照$a_{i,1}-a_{i,2}$从小到大排序

在堆中维护三元组$(S,x,y)$,按照$S$从大到小维护(即堆顶$S$最大),初始在堆中加入$(sum_{i=1}^{n}a_{i,1},1,1)$

每一次取出堆顶的三元组$(S,x,y)$并输出$S$,接下来:

1.若$y<c_{x}$,则加入三元组$(S-(a_{x,y}-a_{x,y+1}),x,y+1)$

2.若$x<n$,则加入三元组$(S-(a_{x+1,1}-a_{x+1,2}),x+1,2,y)$

3.若$x<n$且$y=2$且$x e 1$,则加入三元组$(S-(a_{x+1,1}-a_{x+1,2})+(a_{x,1}-a_{x,2}),x+1,2,y)$

显然这一做法的复杂度为$o(nlog n)$,下面来证明其正确性——

用$d_{i}$去描述一组方案,即表示选择$a_{i,d_{i}}$,其能力值之和即$sum_{i=1}^{n}a_{i,d_{i}}$

考虑一组方案$d_{i}$,我们对其执行下面两种变化:

1.若$d_{x}>1$,令$d_{x}$减小1

2.若$d_{x}=1$且$d_{x+1}=2$(其中$x<1$),交换$d_{x}$和$d_{x+1}$

不难证明,这样得到的方案一定不劣于$d_{i}$(根据排序即可得到)

由此,问题也可以看作证明每一个不在堆中且未被选择的方案(以下称为判定方案)$d_{i}$,都存在一个堆中的三元组$(S,x,y)$以及其所对应的方案$d'_{i}$,满足$forall 1le i<x,d'_{i}=d_{i}$且$d_{i}$可以变为$d'_{i}$

(关于三元组所对应的方案$d'_{i}$,可以通过$S$的变化感性理解,显然其满足$d'_{x}=y,forall x<ile n,d'_{i}=1$)

关于如何将$d_{i}$变为$d'_{i}$,由于上述特殊条件,显然有以下策略:

1.若$d_{x}>d'_{x}=y$,则先将$forall x<ile n,d_{i}$都通过1操作变为1,最后再将$d_{x}$减小为$d'_{x}$

2.若$d_{x}=d'_{x}=y$,若$forall x<kle n,d_{k}=d'_{k}$即已经完成,否则即$exists x<kle n,d_{k} e d'_{k}$($d_{k}>d'_{k}=1$)

取其中最小的$k$,对于$iin (k,n]$都直接将$d_{i}$通过1操作变为1,再将$d_{k}$变为2,由于$forall x<i<k,d_{i}=d'_{i}=1$,因此直接通过2操作不断交换$d_{k-1}$和$d_{k}$、$d_{k-2}$和$d_{k}$……直至$d_{x+1}=2$,再对$x+1$执行1操作即可

3.若$d_{x}<d'_{x}=y$,由于增加$d_{x}$只有2操作,且只能增加为2,因此必然$y=2$($d_{x}=1$)

更进一步的,若$forall x<kle n,d_{k}=d'_{k}=1$即无解,否则类似前面,将$d_{x+1}$变为2,再交换$d_{x}$和$d_{x+1}$即可

(上述策略并不唯一,但这样有助于下面的分析)

仍然回到最初的命题,即证明判定方案$d_{i}$都存在都存在一个堆中的三元组$(S,x,y)$以及其所对应的方案$d'_{i}$,满足$forall 1le i<x,d'_{i}=d_{i}$且$d_{i}$可以变为$d'_{i}$

归纳此命题成立,考虑当我们删除$(S,x,y)$后(设其对应的方案为$d'_{i}$),所有能变为$d'_{i}$的判定方案$d_{i}$(注意这里有$d e d'$,因为$d'$已经被选择),按照上述策略将其操作为$d'$,并撤销最后一次操作,将所有可能得到的方案(即撤销最后一次操作后)都加入,显然仍然满足此条件(每一个$d_{i}$都可以变化为撤销最后一次操作后所产生的方案)

考虑最后一次操作,实际上只有对$x$执行1操作、对$x+1$执行1操作以及交换$d_{x}$和$d_{x+1}$,因此得到的方案也只有3种,即前面的3种转移所得到的方案(显然方案也符合之前的定义)

另外,我们还需要说明方案不会重复——

显然只需要考虑两个不同的方案,分别选择了一种操作后变成了相同的方案,由于$y+1>2$显然不可能

(一个例外是$(1,1)$和$(1,2)$分别使用第2和3种转移都会产生$(2,1)$,限制$x=1$时不能选择第3种转移即可)

综上,即证明正确性

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 100005
 4 #define ll long long
 5 struct Data{
 6     int x,y;
 7     ll ans;
 8     bool operator < (const Data &k)const{
 9         if (ans!=k.ans)return ans>k.ans;
10         return make_pair(x,y)<make_pair(k.x,k.y);
11     }
12 };
13 multiset<Data>s;
14 vector<int>a[N];
15 int n,m,x,y,id[N];
16 ll sum;
17 bool cmp1(int x,int y){
18     return x>y;
19 }
20 bool cmp2(int x,int y){
21     return a[x][0]-a[x][1]<a[y][0]-a[y][1];
22 }
23 int main(){
24     scanf("%d%d",&n,&m);
25     for(int i=1;i<=n;i++){
26         scanf("%d",&x);
27         for(int j=1;j<=x;j++){
28             scanf("%d",&y);
29             a[i].push_back(y);
30         }
31         sort(a[i].begin(),a[i].end(),cmp1);
32         sum+=a[i][0];
33         if (x==1){
34             n--;
35             a[i--].clear();
36         }
37     }
38     for(int i=1;i<=n;i++)id[i]=i;
39     sort(id+1,id+n+1,cmp2);
40     s.insert(Data{1,0,sum});
41     for(int i=1;i<=m;i++){
42         Data o=(*s.begin());
43         s.erase(s.begin());
44         x=o.x,y=o.y,sum=o.ans;
45         printf("%lld ",sum);
46         if (y+1<a[id[x]].size())s.insert(Data{x,y+1,sum-(a[id[x]][y]-a[id[x]][y+1])});
47         if (x<n)s.insert(Data{x+1,1,sum-(a[id[x+1]][0]-a[id[x+1]][1])});
48         if ((x<n)&&(y==1)&&(x!=1))s.insert(Data{x+1,1,sum-(a[id[x+1]][0]-a[id[x+1]][1])+(a[id[x]][0]-a[id[x]][1])});
49     } 
50 } 
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/14848260.html