题目
题目链接:https://codeforces.com/problemset/problem/618/F
给你两个可重集 \(A,B\),\(A,B\) 的元素个数都为 \(n\) 且 \(n⩽1000000\),它们中每个元素的大小 \(x∈[1,n]\)。请你分别找出 \(A,B\) 的可重子集,使得它们中的元素之和相等。
思路
记两个数组的前缀和分别为 \(a,b\),设 \(a[n]\geq b[n]\)。对于每一个 \(a[i]\),求出小于 \(a[i]\) 且最接近 \(a[i]\) 的 \(b[j]\),令 \(c[i]\) 表示 \(a[i]-b[j]\)。
如果存在两个 \(c[i]\) 和 \(c[j]\) 相等,那么就一定存在答案。由于 \(0\leq c< n\),而一共有 \(c[0]\sim c[n]\) 共 \(n+1\) 个不同的答案,所以一定有答案,且一定有连续的答案。
时间复杂度 \(O(n)\)。
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1000010;
int n,pos[N][2];
ll a[N],b[N];
bool flag;
ll read()
{
ll d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
int main()
{
n=read();
for (int i=1;i<=n;i++)
a[i]=a[i-1]+read();
for (int i=1;i<=n;i++)
b[i]=b[i-1]+read();
if (a[n]<b[n])
{
for (int i=1;i<=n;i++)
swap(a[i],b[i]);
flag=1;
}
memset(pos,-1,sizeof(pos));
for (int i=0,j=0;i<=n;i++)
{
while (j<n && a[i]>=b[j+1]) j++;
int k=a[i]-b[j];
if (pos[k][0]>=0)
{
if (flag) swap(i,j),swap(pos[k][0],pos[k][1]);
printf("%d\n",i-pos[k][0]);
for (int l=pos[k][0]+1;l<=i;l++) printf("%d ",l);
printf("\n%d\n",j-pos[k][1]);
for (int l=pos[k][1]+1;l<=j;l++) printf("%d ",l);
return 0;
}
pos[k][0]=i; pos[k][1]=j;
}
return 0;
}