鸽巢原理运用

给定长为 \(m\) 的序列 \(a\),求一组 \(k,l\) 使得 \(m|\sum\limits^l_{i=k}a_i\)

第一行输入 \(m\)

第二行输入 \(m\) 个数字,表示序列 \(a\)

输出 \(k,l\)

保证 \(0\le m\le 2*10^5,0\le a_i\le 10^3\)

保证数据在 \(int\) 范围内。

题解:证明存在性。若 \(a_1,a_1+a_2,a_1+a_2+a_3,\cdots,a_1+a_2+a_3+\cdots+a_m\) 中有任何一个可以被 \(m\) 整除那么结论就成立。设这 \(m\) 个和模 \(m\) 的余数为 \(r1, r2,r3,\cdots,r_m\),因为 \(x \mod m\) 的值只能是 \([0,m-1]\),所以定有两个余数相同,这里用到鸽巢原理。

设这两个相同余数为 \(a_1+a_2+\cdots+a_k=bm+r\)\(a_1+a_2+\cdots+a_l=cm+r\)。我们将其作差得到 \(a_{k+1}+\cdots + a_l=(c-b)m\),故 \(a_{k+1}+\cdots+a_l\) 能被 \(m\) 整除。

那么这就优化了暴力枚举,我们用前缀和判同余即可,时间复杂度达到 \(O(n)\)

代码:

#include <bits/stdc++.h>
#define rei register int
#define ll long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define rep(i, s, n, c) for (register int i = s; i <= n; i+=c)
#define repd(i, s, n, c) for (register int i = s; i >= n; i-=c)
#define CHECK cout<<"WALKED"<<endl;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
#define pb push_back
#define ls id<<1
#define rs id<<1|1
const int INF = INT_MAX;
long long binpow(long long a, long long b, ll mod){long long res = 1;  while (b > 0){if (b & 1) res = res * a % mod;a = a * a % mod;  b >>= 1;  }  return res;}

using namespace std;
int a[200005], b[200005];
int main()
{
	int m = read();
	rep (i, 1, m, 1) {
		a[i] = read();
		a[i] += a[i - 1];
	}
	rep (i, 1, m, 1) {
		int r = a[i] % m;
		if (!b[r]) {
			b[r] = i + 1;
		} else {
			printf("%d %d\n", b[r], i);
			break;
		}
	}
    return 0;
}
posted @ 2023-01-06 17:11  Vegdie  阅读(34)  评论(0编辑  收藏  举报