抽屉原理(鸽笼原理)的应用

本博客为 OI 与 组合数学 (Combinatorics) 的结合

n + 1 n+1 n+1 个物体放入 n n n 个抽屉中,至少存在一个抽屉有两个物体

例题:Find a Multiple

https://acm.timus.ru/problem.aspx?space=1&num=1032

https://vjudge.net.cn/problem/EOlymp-5621

http://poj.org/problem?id=2356

以上三个网址都可以,看个人喜好,推荐用第二个https://vjudge.net.cn/problem/EOlymp-5621

题意

给你 n n n 个数,选出若干个使得他们的和为 n n n 的整数倍,输出这些数

思路

结论:一定存在一段连续子序列,使得他们的和为 n n n 的倍数

证明:

定义数组 s s s 为 数组 a a a 的前缀和 n n n 取模的值 s 0 = 0 , s 1 = a 1   m o d   n , s 2 = ( a 1 + a 2 )   m o d   n ,   . . .   , s n = ( a 1 + a 2 + . . . + a n )   m o d   n s_0=0, s_1=a_1 \bmod n,s_2=(a_1+a_2)\bmod n,\ ...\ ,s_n=(a_1+a_2+...+a_n) \bmod n s0=0,s1=a1modn,s2=(a1+a2)modn, ... ,sn=(a1+a2+...+an)modn

则共有 n + 1 n+1 n+1 个整数(注意下标是从 0 0 0 开始的)的范围是 0 ≤ s i ≤ n − 1 0 \le s_i \le n-1 0sin1 s i s_i si 为整数

∵ 这个区间内的整数只有 n n n

∴ 必存在两个 s i s_i si 相等。

s p = s q s_p=s_q sp=sq ,则 s q − s p = 0 s_q-s_p=0 sqsp=0
{      s q − s p = ∑ i = 1 q   m o d     n − ∑ i = 1 p   m o d     n = ∑ i = p + 1 q   m o d     n = 0 \begin{cases} \ \ \ \ s_q-s_p \\ =\displaystyle \sum_{i=1}^q \bmod\ n - \sum_{i=1}^p \bmod \ n \\ =\displaystyle \sum_{i=p+1}^q \bmod \ n \\ =0 \end{cases}     sqsp=i=1qmod ni=1pmod n=i=p+1qmod n=0
即: ( a p + 1 + a p + 2 +   . . .   + a q )   m o d   n = 0 (a_{p+1}+a_{p+2}+\ ...\ +a_q) \bmod n =0 (ap+1+ap+2+ ... +aq)modn=0

C++ 代码

无多余空格代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=200005;
int n;
int v[maxn];
int sum[maxn];
vector<pair<int,int> > ve;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>v[i];
	}
	for(int i=1;i<=n;i++){
		sum[i]=(sum[i-1]+v[i])%n;
		ve.push_back(make_pair(sum[i],i));
	}
	sort(ve.begin(),ve.end());
	int i;
	for(i=1;i<ve.size();i++){
		if(ve[i].first==ve[i-1].first){
			break;
		}
	}
	cout<<ve[i].second-ve[i-1].second<<endl;
	for(int s=ve[i-1].second+1;s<=ve[i].second;s++){
		cout<<v[s]<<endl;
	}
	return 0;
}

标准版代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200005;
int n;
int v[maxn];
int sum[maxn];
vector<pair<int, int> > ve;
int main(){
	cin >> n;
	for (int i = 1; i <= n;i ++) {
		cin >> v[i];
	}
	for (int i = 1; i <= n;i ++) {
		sum[i] = (sum[i-1] + v[i]) % n;
		ve.push_back(make_pair(sum[i], i));
	}
	sort (ve.begin(), ve.end());
	int i;
	for (i = 1; i < ve.size(); i ++) {
		if (ve[i].first == ve[i-1].first) {
			break;
		}
	}
	cout << ve[i].second - ve[i-1].second << endl;
	for (int s = ve[i-1].second+1; s <= ve[i].second; s ++) {
		cout << v[s] << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值