欢迎神犇吊打|

Hanx16Msgr

园龄:2年8个月粉丝:12关注:3

2022-08-19 10:52阅读: 45评论: 0推荐: 0

CF618F Double Knapsack

Double Knapsack

CF618F (Luogu)

题面翻译

给你两个可重集 A,BA,B 的元素个数都为 n,它们中每个元素的大小 x[1,n]。请你分别找出 A,B 的子集,使得它们中的元素之和相等。

n106

题目描述

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 ka , the size of the corresponding subset of A . The second line should contain ka distinct integers, the indices of the subset of A .

The third line should contain a single integer kb , the size of the corresponding subset of B . The fourth line should contain kb 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 数组的前缀和为 sumab 数组的前缀和为 sumb。定义 ci 为最靠右的下标并且满足 sumaisumbci。根据定义有:

sumai<sumbci+1

sumai<sumbci+bci+1

移项得:

sumaisumbci<bci+1

因为 bci+1 的值域是 [1,n] 的,因此 0sumaisumbci<n,总共有 n 个可能的取值,而 i 的值域是 [0,n] 的,也就是说有 n+1 种可能的情况,那么根据抽屉原理,一定会存在有一组满足:

sumaisumbci=sumajsumbcj

移项得:

sumaisumaj=sumbcisumbcj

这不就裸的前缀和式子?因此可以证明一定存在答案,并且答案一定是在两个序列中连续的一段子序列。

考虑如何求出 c。不难发现,因为 suma 是递增的,因此 c 也一定是单调不降的,所以用一个指针来计算 c 即可,同时计算此时的 sumaisumbci,如果这一个值之前出现过,那么就是找到了一组可行的答案,否则标记这个值,继续往后找。

Code

需要注意因为序列中的数字值域是 [1,1×106] 的,并且有 1×106 个数,所以前缀和最大可以到 1×1012,所以记得开 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;
}

posted @   Hanx16Msgr  阅读(45)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起