Coins
Time Limit: 8000/8000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 612 Accepted Submission(s): 144
Problem Description
There are n groups of coins, and the i-th group contains two coins valued at ai and bi. Now you want to pick exactly k coins out of them. However, there is a restriction - you can not pick the second coin valued at bi in the i-th group without picking the other one in the same group. In other words, in the i-th group, you can
- pick none of the two coins;
- pick only the first one valued at ai; or
- pick both of them.
We now want to know the maximum sum of the k picked coins' values, denoted by f(k).
Furthermore, we want to know f(1),f(2),⋯,f(2n).
Input
The input contains several test cases, and the first line contains a single integer T (1≤T≤90), the number of test cases.
For each test case, the first line contains an integer n (1≤n≤100000), indicating the number of coin groups.
Each of the following n lines contains two integers ai,bi (1≤ai,bi≤10000) indicating the coin values in that group.
It is guaranteed that the sum of n in all test cases does not exceed 2100000.
Output
For each test group, just output 2n integers in a single line representing f(1),f(2),⋯,f(2n), and adjacent integers should be separated by one space.
Sample Input
2
3
1 2
1 4
4 2
2
1 3
3 2
Sample Output
4 6 9 11 12 14
3 5 7 9
题意
有n组硬币,每组两个硬币,权值分别为ai,bi,当你选了某一组中的bi硬币时,这一组的ai硬币也必须被选(当然你也可以不选某一组,或者只选ai)。现在,对于每一个i∈[1,2n],你要求出恰好选i个硬币的最大权值和。
题解
先附上原题解
其实这道题即便h不是单增函数也可以做(废话)
前面的思路都很简单
主要讲一下决策单调性优化
首先我们有一个状态转移方程
如果函数是一个单增且增量递减的函数,那么就可以决策单调性优化
我们把叫做点 j 的贡献函数
因为后面的点 i 一定是取 前面所有贡献函数 在i处的最大值来进行转移
我们先把所有的贡献函数画出来,如图:(以为例)
显然我们可以看出:
点2的最优转移是红色线
点3的最优转移是绿色线
点4的最优转移仍然是绿色线
点5的最优转移是深蓝色线
我们发现,如果一个贡献函数被另一个贡献函数超越了,那么它就永远无法反超了
所以我们只需要维护最上面一层的贡献函数就好了
我们可以用一个单调队列来维护
当一个在队尾的贡献函数在他成为第一名之前被别人超过,那么它永远也当不了第一名,就可以直接弹出了
为了判断这个条件,我们需要二分来求两个函数的交点,利用交点的函数值来进行判断(具体看代码)
当一个在队首的贡献函数已经无法更新当前答案了,它也可以被弹出了
然后回到这个题
我们发现它的转移式子可以化为:
因为g[i-j]是单增且增量递减的函数,所以就可以看作
然后就决策单调性优化啦
g,h都可以预处理
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005
#define INF 1<<30
pair<int,int> b[N];
int g[N<<1],h[N<<1],f[N<<1];
int n,nn,a[N<<1],cnta,cntb;
int q[N<<1],k[N<<1];
bool cmpa(int a,int b){return a>b;}
bool cmpb(pair<int,int> a,pair<int,int> b){return a.first+a.second>b.first+b.second;}
int calc(int i,int j){return h[j]+g[i-j];}
int getk(int i,int j)
{
int l=j,r=nn,mid;
while(l<r){
mid=(l+r)>>1;
if(calc(mid,i)<=calc(mid,j))r=mid;
else l=mid+1;
}
if(calc(l,i)<=calc(l,j))
return l;
return r+1;
}
void F()
{
f[0]=0;memset(k,0,sizeof(k));
int he=1,ta=0,i;
q[++ta]=0;
for(i=1;i<=nn;i++){
while(he<ta&&calc(k[ta-1],q[ta])<=calc(k[ta-1],i))
ta--;
k[ta]=getk(q[ta],i);q[++ta]=i;
while(he<ta&&k[he]<=i)
he++;
f[i]=calc(i,q[he]);
}
}
void G()
{
memset(g,-0x7f,sizeof(g));g[0]=0;
sort(a+1,a+cnta+1,cmpa);
for(int i=1;i<=cnta;i++)
g[i]=g[i-1]+a[i];
}
int mxa[N],mib[N];
void H()
{
memset(h,-0x7f,sizeof(h));h[0]=0;
sort(b+1,b+cntb+1,cmpb);
mxa[cntb+1]=-INF;
int i;
for(i=cntb;i>=1;i--)
mxa[i]=max(mxa[i+1],b[i].first);
mib[0]=INF;
for(i=1;i<=cntb;i++)
mib[i]=min(mib[i-1],b[i].second);
for(i=1;i<=cntb;i++)
h[i<<1]=h[(i-1)<<1]+b[i].first+b[i].second;
for(i=0;i<=cntb;i++){
h[2*i+1]=max(h[2*i+1],h[i<<1]+mxa[i+1]);
if(i)
h[2*i-1]=max(h[2*i-1],h[i<<1]-mib[i]);
}
}
int main()
{
int T,x,y,i;
scanf("%d",&T);
while(T--){
scanf("%d",&n);nn=n<<1;
cntb=cnta=0;
for(i=1;i<=n;i++){
scanf("%d%d",&x,&y);
if(x>y){
a[++cnta]=x;
a[++cnta]=y;
}
else b[++cntb]=make_pair(x,y);
}
G();H();F();
printf("%d",f[1]);
for(i=2;i<=nn;i++)
printf(" %d",f[i]);
printf("\n");
}
}