【CodeForces 618F】Double Knapsack

链接:

洛谷

博客园

题目大意:

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

正文:

这是一道非常牛逼的构造题。

\(A,B\) 分别为 \(a,b\) 的前缀和。猜想有解且一定连续

\(A_n\leq B_n\),对于每个 \(i\in[1,n]\),都能找到对应的 \(j\leq n\) 使得 \(B_j\leq A_i,B_{j+1}>A_i\)。也就是 \(A_i<B_j+b_{j+1}\),得到 \(0\leq A_i-B_j<n\),所以一共有 \(n+1\)\(A_i-B_j\)。但又因为只有 \(n\)\(i\),鸽笼原理,必有 \(A_{i_1}-B_{j_1}=A_{i_2}-B_{j_2}\)\(\mathcal{O}(n)\) 桶排即可。

代码:

const int N = 2e6 + 10;

inline ll Read()
{
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n;
ll a[N], b[N];
ll bucket[N][2];
bool flag;

void Sol1(int i, int j)
{
	printf ("%d\n", i - bucket[a[i] - b[j]][0]);
	for (int k = bucket[a[i] - b[j]][0] + 1; k <= i; k++)
		printf ("%d ", k);
}

void Sol2(int i, int j)
{
	printf ("%d\n", j - bucket[a[i] - b[j]][1]);
	for (int k = bucket[a[i] - b[j]][1] + 1; k <= j; k++)
		printf ("%d ", k);
}

int main()
{
//	freopen(".in", "r", stdin);
//	freopen(".out", "w", stdout);
	memset (bucket, -1, sizeof bucket);
	n = Read();
	for (int i = 1; i <= n; i++) a[i] = Read() + a[i - 1];
	for (int i = 1; i <= n; i++) b[i] = Read() + b[i - 1];
	if (a[n] < b[n]) swap (a, b), flag = 1;
	for (int i = 0, j = 0; i <= n; i++)
	{
		for (; b[j] <= a[i] && j <= n; j++);
		j--;
		if (~bucket[a[i] - b[j]][0]) 
		{
			if (flag) {Sol2(i, j); putchar(10); Sol1(i, j);} 
			else {Sol1(i, j); putchar(10); Sol2(i, j);} 
			break;
		}
		else bucket[a[i] - b[j]][0] = i, bucket[a[i] - b[j]][1] = j;
	}
	return 0;
}
posted @ 2021-10-19 21:42  Jayun  阅读(44)  评论(0编辑  收藏  举报