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不是单增函数也可以做(废话)

前面的思路都很简单

主要讲一下决策单调性优化

 

首先我们有一个状态转移方程

f[i]=max(f[j]+g_{j}(i)) (1\leq j<i)

如果g_{j}(i)函数是一个单增且增量递减的函数,那么就可以决策单调性优化

我们把f[j]+g_{j}(i)叫做点 j 的贡献函数

因为后面的点 i 一定是取  前面所有贡献函数  在i处的最大值来进行转移

我们先把所有的贡献函数画出来,如图:(以g_{j}(x)=\sqrt{x-j}为例)

显然我们可以看出:

点2的最优转移是红色线

点3的最优转移是绿色线

点4的最优转移仍然是绿色线

点5的最优转移是深蓝色线

 

我们发现,如果一个贡献函数被另一个贡献函数超越了,那么它就永远无法反超了

所以我们只需要维护最上面一层的贡献函数就好了

我们可以用一个单调队列来维护

当一个在队尾的贡献函数在他成为第一名之前被别人超过,那么它永远也当不了第一名,就可以直接弹出了

为了判断这个条件,我们需要二分来求两个函数的交点,利用交点的函数值来进行判断(具体看代码)

当一个在队首的贡献函数已经无法更新当前答案了,它也可以被弹出了

 

 

然后回到这个题

我们发现它的转移式子可以化为:f[i]=max(h[j]+g[i-j])(0\leq j<i)

因为g[i-j]是单增且增量递减的函数,所以就可以看作g_{j}(i)

然后就决策单调性优化啦

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");
	}
}