【CodeForces 618F】Double Knapsack

链接:

洛谷

博客园

题目大意:

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

正文:

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

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

(A_nleq B_n),对于每个 (iin[1,n]),都能找到对应的 (jleq n) 使得 (B_jleq A_i,B_{j+1}>A_i)。也就是 (A_i<B_j+b_{j+1}),得到 (0leq 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
", 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
", 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;
}
原文地址:https://www.cnblogs.com/GJY-JURUO/p/15426445.html