[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.若$1<x<n$且$y=2$且$x\ne 1$,则加入三元组$(S-(a_{x+1,1}-a_{x+1,2})+(a_{x,1}-a_{x,2}),x+1,2,y)$
显然这恰能不重不漏的得到所有方案且权值和单调不降,即具有正确性
时间复杂度为$o(n\log n)$,可以通过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }