CF618F Double Knapsack
Double Knapsack
题面翻译
给你两个可重集 \(A, B\),\(A, B\) 的元素个数都为 \(n\),它们中每个元素的大小 \(x\in [1,n]\)。请你分别找出 \(A, B\) 的子集,使得它们中的元素之和相等。
\(n\leq 10^6\)。
题目描述
You are given two multisets $ A $ and $ B $ . Each multiset has exactly $ n $ integers each between $ 1 $ and $ n $ inclusive. Multisets may contain multiple copies of the same number.
You would like to find a nonempty subset of $ A $ and a nonempty subset of $ B $ such that the sum of elements in these subsets are equal. Subsets are also multisets, i.e. they can contain elements with equal values.
If no solution exists, print $ -1 $ . Otherwise, print the indices of elements in any such subsets of $ A $ and $ B $ that have the same sum.
输入格式
The first line of the input contains a single integer $ n $ ( $ 1<=n<=1000000 $ ) — the size of both multisets.
The second line contains $ n $ integers, denoting the elements of $ A $ . Each element will be between $ 1 $ and $ n $ inclusive.
The third line contains $ n $ integers, denoting the elements of $ B $ . Each element will be between $ 1 $ and $ n $ inclusive.
输出格式
If there is no solution, print a single integer $ -1 $ . Otherwise, your solution should be printed on four lines.
The first line should contain a single integer $ k_{a} $ , the size of the corresponding subset of $ A $ . The second line should contain $ k_{a} $ distinct integers, the indices of the subset of $ A $ .
The third line should contain a single integer $ k_{b} $ , the size of the corresponding subset of $ B $ . The fourth line should contain $ k_{b} $ distinct integers, the indices of the subset of $ B $ .
Elements in both sets are numbered from $ 1 $ to $ n $ . If there are multiple possible solutions, print any of them.
样例 #1
样例输入 #1
10
10 10 10 10 10 10 10 10 10 10
10 9 8 7 6 5 4 3 2 1
样例输出 #1
1
2
3
5 8 10
样例 #2
样例输入 #2
5
4 4 3 3 3
2 2 2 2 5
样例输出 #2
2
2 3
2
3 5
Solution
一道思维题,需要有比较强的想象力才可能想到正解。
我们先假设这个问题一定存在一组符合题意的解,而且这组解在两个序列中还是连续的。定义 \(a\) 数组的前缀和为 \(suma\),\(b\) 数组的前缀和为 \(sumb\)。定义 \(c_i\) 为最靠右的下标并且满足 \(suma_i \ge sumb_{c_i}\)。根据定义有:
移项得:
因为 \(b_{c_i+1}\) 的值域是 \([1,n]\) 的,因此 \(0\le suma_i-sumb_{c_i}<n\),总共有 \(n\) 个可能的取值,而 \(i\) 的值域是 \([0,n]\) 的,也就是说有 \(n+1\) 种可能的情况,那么根据抽屉原理,一定会存在有一组满足:
移项得:
这不就裸的前缀和式子?因此可以证明一定存在答案,并且答案一定是在两个序列中连续的一段子序列。
考虑如何求出 \(c\)。不难发现,因为 \(suma\) 是递增的,因此 \(c\) 也一定是单调不降的,所以用一个指针来计算 \(c\) 即可,同时计算此时的 \(suma_i-sumb_{c_i}\),如果这一个值之前出现过,那么就是找到了一组可行的答案,否则标记这个值,继续往后找。
Code
需要注意因为序列中的数字值域是 \([1,1\times 10^6]\) 的,并且有 \(1\times 10^6\) 个数,所以前缀和最大可以到 \(1\times 10^{12}\),所以记得开 long long
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
//#define int long long
using namespace std;
template<typename T> void read(T &k)
{
k=0;T flag=1;char b=getchar();
while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
while (isdigit(b)) {k=k*10+b-48;b=getchar();}
k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
const int _SIZE=1e6;
int n,a[_SIZE+5],b[_SIZE+5];
long long suma[_SIZE+5],sumb[_SIZE+5],id[_SIZE+5][2];
bool flag[_SIZE+5];
signed main()
{
read(n);
for (int i=1;i<=n;i++) read(a[i]),suma[i]=suma[i-1]+a[i];
for (int i=1;i<=n;i++) read(b[i]),sumb[i]=sumb[i-1]+b[i];
bool swaped=0;
if (suma[n]>sumb[n]) swaped=1,swap(suma,sumb);
int j=0,al,ar,bl,br;
for (int i=0;i<=n;i++)
{
while (suma[i]>=sumb[j] && j<=n) j++;j--;
if (flag[suma[i]-sumb[j]])
{
al=id[suma[i]-sumb[j]][0]+1;ar=i;
bl=id[suma[i]-sumb[j]][1]+1;br=j;
break;
}
flag[suma[i]-sumb[j]]=1;
id[suma[i]-sumb[j]][0]=i;
id[suma[i]-sumb[j]][1]=j;
}
if (swaped) swap(al,bl),swap(ar,br);
writewith(ar-al+1,'\n');
for (int i=al;i<=ar;i++) writewith(i,' ');puts("");
writewith(br-bl+1,'\n');
for (int i=bl;i<=br;i++) writewith(i,' ');puts("");
return 0;
}