CF618F Double Knapsack

题意简化

给定两个大小为 n 的集合A,B,要求在每个集合中选出一个子集,使得两个选出来的子集元素和相等
元素范围在 1~n ,n<=1e5
题目连接

题解

考虑前缀和
令A集合的前缀和为SA,B的前缀和SB (有点奇怪,但不重要...)
设SA[n]<SB[n],从0到n枚举i, 对于每个SA[i],找到最小的 SB[j] 使得 SB[j]>=SA[i], 所以\(0<=SB[j]-SA[i]<n\),
所以总共会有n+1个 \(SB[j]-SA[i]\) 而值域却只有n,所以可以保证会有两个及以上重复的数字 (保证有解)
即:
存在 \(SB[j1]-SA[i1]==SB[j2]-SA[i2]\)
显然 B集合中 j2 ~ j1 这段区间的元素和等于A集合中 i2 到 i1 的元素和

代码

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define get getchar()
#define in inline
in int read()
{
	int t=0; char ch=get;
	while(ch<'0' || ch>'9') ch=get;
	while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
	return t;
}
const int _=2e6+6;
int n,a[_],b[_];
ll suma[_],sumb[_],x[_],y[_];
bool f[_];
int main()
{
	n=read();
	for(re int i=1;i<=n;i++)
		a[i]=read(),suma[i]=suma[i-1]+a[i];
	for(re int i=1;i<=n;i++)
		b[i]=read(),sumb[i]=sumb[i-1]+b[i];
	int fff=0;
	if(sumb[n]<suma[n]) {
		for(re int i=1;i<=n;i++) {
			ll t=a[i];
			a[i]=b[i],b[i]=t;
			t=suma[i];
			suma[i]=sumb[i],sumb[i]=t;
		}
		fff=1;
	}
	int j=0;
	for(re int i=0;i<=n;i++) {
		while(sumb[j]<suma[i]) j++;
		int k=sumb[j]-suma[i];
		if(f[k])
		{
			if(!fff) {
				cout<<i-x[k]<<endl;
				for(re int p=x[k]+1;p<=i;p++)
					cout<<p<<' ';cout<<endl;
				cout<<j-y[k]<<endl;
				for(re int p=y[k]+1;p<=j;p++)
					cout<<p<<' ';cout<<endl;
			}
			else {
				cout<<j-y[k]<<endl;
				for(re int p=y[k]+1;p<=j;p++)
					cout<<p<<' ';cout<<endl;
				cout<<i-x[k]<<endl;
				for(re int p=x[k]+1;p<=i;p++)
					cout<<p<<' ';cout<<endl;
			}
			return 0;
		}
		f[k]=1;
		x[k]=i,y[k]=j;
	}
}

posted @ 2019-10-25 15:13  yzhx  阅读(136)  评论(1编辑  收藏  举报