給 N (≤ 10000) 個正整數(每個 ≤ 15000),找非空子集使和為 N 的倍數。鴿籠原理保證必有解。
前綴和 sum[i] = (a[1]+...+a[i]) % N,共 N 個值,取值 [0, N-1]。
sum[i] == 0,前 i 個數即為解。N 個值落入 N-1 個非零餘數,必有 i < j 使 sum[i] == sum[j]。區間 (i, j] 的和模 N 為 0。N。pos[rem],pos[0]=0。i=1..N,若 pos[sum[i]] 已存在,輸出區間 (pos[sum[i]], i]。#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 100010;
int n, a[MAXN], sum[MAXN], pos[MAXN];
int main() {
while (scanf("%d", &n) == 1) {
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
sum[0] = 0;
for (int i = 1; i <= n; i++) sum[i] = (sum[i-1] + a[i]) % n;
memset(pos, -1, sizeof(pos));
pos[0] = 0;
for (int i = 1; i <= n; i++) {
if (pos[sum[i]] == -1) pos[sum[i]] = i;
else {
int cnt = i - pos[sum[i]];
printf("%d\n", cnt);
for (int j = pos[sum[i]] + 1; j <= i; j++) printf("%d\n", a[j]);
break;
}
}
}
return 0;
}
MAXN=100010 足夠。雙重循環枚舉所有區間 O(N²),N=10000 會 TLE。鴿籠原理將問題降維為 O(N) 線性掃描。
「N 個抽屜放 N+1 隻鴿子(含 sum[0]=0),至少一個抽屜 ≥ 2 隻。」本題中抽屜是餘數 0..N-1,鴿子是前綴和。
O(N)。O(N)。POJ 2356 是鴿籠原理在「子集和取模」問題中應用的教科書級範例,代碼極短、邏輯鐵clad,是競賽程序員必背模板之一。