题解 CF618F Double Knapsack
作者:互联网
首先,这个要求的解的形式太过自由,选择一个子集不是很好处理。于是我们可以套路地对其加以限制,变成选择两段区间,使他们和相同。
令 $sa_i$ 和 $sb_i$ 分别表示 $a$ 和 $b$ 序列的前缀和,此处我们假设 $sa_n\le sb_n$。
那么我们可以枚举 $i$,找到一个最大的满足 $sb_j\le sa_i$ 的 $j$,由于 $sa_n\le sb_n$,所以容易发现,$sa_i-sb_j \in [0,n-1]$。对于 $n+1$ 种 $sa_i-sb_j$ 只有 $n$ 种取值,根据抽屉原理可知肯定有两对 $sa_i-sb_j$ 是相同的:$sa_{i_1}-sb_{j_1}=sa_{i_2}-sb_{j_2}$,可以得到 $sa_{i_2}-sa_{i_1}=sb_{j_2}-sb_{j_1}$ 所以所求答案的区间即为 $a[i_1+1...i_2]$ 和 $b[j_1+1...j_2]$。
#include <bits/stdc++.h> using namespace std; #define ll long long #define fi first #define se second const int N = 1e6 + 5; int n, a[N], b[N], buki[N], bukj[N], ok; ll sa[N], sb[N]; int main() { memset(buki, -1, sizeof(buki)); memset(bukj, -1, sizeof(bukj)); scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]), sa[i] = sa[i - 1] + a[i]; for(int i = 1; i <= n; i++) scanf("%d", &b[i]), sb[i] = sb[i - 1] + b[i]; if(sa[n] > sb[n]) { ok = 1; for(int i = 1; i <= n; i++) swap(a[i], b[i]), swap(sa[i], sb[i]); } for(int i = 0, j = 0; i <= n; i++) { while(j + 1 <= n && sa[i] >= sb[j + 1]) j++; if(buki[sa[i] - sb[j]] != -1) { if(!ok) { printf("%d\n", i - buki[sa[i] - sb[j]]); for(int k = buki[sa[i] - sb[j]] + 1; k <= i; k++) printf("%d ", k); printf("\n%d\n", j - bukj[sa[i] - sb[j]]); for(int k = bukj[sa[i] - sb[j]] + 1; k <= j; k++) printf("%d ", k); printf("\n"); } else { printf("%d\n", j - bukj[sa[i] - sb[j]]); for(int k = bukj[sa[i] - sb[j]] + 1; k <= j; k++) printf("%d ", k); printf("\n%d\n", i - buki[sa[i] - sb[j]]); for(int k = buki[sa[i] - sb[j]] + 1; k <= i; k++) printf("%d ", k); printf("\n"); } return 0; } buki[sa[i] - sb[j]] = i, bukj[sa[i] - sb[j]] = j; } return 0; }
标签:le,CF618F,题解,int,Knapsack,bukj,sb,sa,buki 来源: https://www.cnblogs.com/qshjydzh/p/16064088.html