CF618F Double Knapsack 构造、抽屉原理

传送门


首先,选取子集的限制太宽了,子集似乎只能枚举,不是很好做。考虑加强限制条件:将“选取子集”的限制变为“选取子序列”的限制。在接下来的讨论中我们将会知道:将限制控制得更紧,问题也一定会有解。

现在我们需要求(A,B)的两个子序列,满足两者的和相等。显然可以前缀和,然后就不会做了qwq

考虑下面的算法:假定(sumlimits_{a in A} a < sumlimits_{b in B} b)(如果相等直接全选),设序列(A)前缀和为(sumA_i),序列(B)前缀和为(sumB_i)

对于(n+1)(sumA_i),在(sumB)中找到最小的大于等于它的元素(sumB_j),那么一定有(sumB_j - sumA_i in [0,n)),可能的(sumB - sumA)(n)种,但是有(n+1)(i,j)

根据抽屉原理,一定会存在两组((i_1,j_1)(i_2,j_2)(i_1 > i_2))满足(sumB_{j_1} - sumA_{i_1} = sumB_{j_2} - sumA_{i_2}),即(sumB_{j_1} - sumB_{j_2} = sumA_{i_1} - sumA_{i_2})。这样我们就找到了一组可行解:在(A)中选择(A_x , x in (i_2 , i_1]),在(B)中选择(B_y , y in (j_2 , j_1])

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<cmath>
#include<random>
//This code is written by Itst
using namespace std;

inline int read(){
	int a = 0;
	char c = getchar();
	bool f = 0;
	while(!isdigit(c) && c != EOF){
		if(c == '-')
			f = 1;
		c = getchar();
	}
	if(c == EOF)
		exit(0);
	while(isdigit(c)){
		a = a * 10 + c - 48;
		c = getchar();
	}
	return f ? -a : a;
}

#define PII pair < int , int >
#define st first
#define nd second
#define ll long long
const int MAXN = 1e6 + 7;
ll A[MAXN] , B[MAXN];
PII pos[MAXN];

signed main(){
#ifndef ONLINE_JUDGE
	freopen("in","r",stdin);
	//freopen("out","w",stdout);
#endif
	int 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();
	bool f = A[N] > B[N];
	if(f) swap(A , B);
	fill(pos , pos + N + 1 , PII(-1 , -1));
	int p = 0;
	for(int i = 0 ; i <= N ; ++i){
		while(B[p] < A[i]) ++p;
		if(pos[B[p] - A[i]] != PII(-1 , -1)){
			PII t = pos[B[p] - A[i]];
			if(!f){
				printf("%d
" , i - t.first);
				for(int j = t.first + 1 ; j <= i ; ++j)
					printf("%d " , j);
				printf("
%d
" , p - t.second);
				for(int j = t.second + 1 ; j <= p ; ++j)
					printf("%d " , j);
			}
			else{
				printf("%d
" , p - t.second);
				for(int j = t.second + 1 ; j <= p ; ++j)
					printf("%d " , j);
				printf("
%d
" , i - t.first);
				for(int j = t.first + 1 ; j <= i ; ++j)
					printf("%d " , j);
			}
			return 0;
		}
		else pos[B[p] - A[i]] = PII(i , p);
	}
	puts("-1");
	return 0;
}
原文地址:https://www.cnblogs.com/Itst/p/10585514.html