【题目地址】
http://acm.pku.edu.cn/JudgeOnline/problem?id=3370
【题目大意】
万圣节邻居i会发给孩子们一定数量的糖果a[i],现在有c个孩子和n户邻居(1<=c<=n<=100000), 为了不引起纠纷,孩子们决定选择性的去某些邻居家索要糖果,使得到的糖果总数可以均分给n个人(只要均分就行,不要求糖果数最多)。
【解题思路】
基本原理:如果n+1个物体放进n个盒子,那么至少有一个盒子包含两个或者更多的物体。考虑一个问题:给出n个数,从中选出若干数使他们的和为n的倍数。
结论:存在若干连续数,它们的和是n的倍数。
证明:设S(k)=a(1)+a(2)+···+a(k).如果存在一个S(k)是n的倍数,那么把前k个数选出来就可以了。否则所有n个S(k)(n个物体)除以n的余数只有1,2,3,···,n-1中可能(n-1个盒子),但现在有n个S(k)(n个物体),由基本原理知,必然有两个不同的和S(i)和S(j)(i<j)除以n的余数相同,故部分和S(j)-S(i)=a(i+1)+···+a(j)是n的倍数。
本题暴力肯定TLE,考虑鸽笼定理:从n个数中取出若干,和为c(c<=n)的倍数。
看另外一个例子:
一个屋子里面有n个人,他们的最大年龄不超过k岁,问是否肯定存在2组人(两组没有重复的人),使得这两组人的年龄和相等。
先看一个简单的:有5个小朋友,年龄(整数)都在1—4岁之间,那么至少有两个人的年龄相同(不要怀疑);
那么本例中,n个人的组合,一共有2^n(2^n个小朋友)种方法,注意到最大情况n的人的年龄和是n* k(小朋友的年龄在1—n*k之间),这样如果2^n>n*k,根据鸽笼原理,一定存在两种组合,他们的年龄和相等,而且没有重复的人(如果重复,把那个人删去就行了)。所以 O(1)的时间就判断出了结果。
此例只是俺的想法,如果有更好的证明方法,欢迎提出。。囧!ACM_DIY群里有大牛证明了,看不懂。。
回到POJ 3370 Halloween treats,看代码(求前S(k)的话int可能溢出,每次取模即可):
【代码】
-
#include <cstdio>
-
#include <cstring>
-
#define maxn 1000005
-
int m[maxn],mod[maxn];
-
int main()
-
{
-
int c,n,i,sum,len,pos;
-
while(scanf("%d%d",&c,&n)&&c+n){
-
memset(mod,-1,sizeof(mod));
-
for(i=sum=len=0;i<n;i++){
-
scanf("%d",&m[i]);
-
if(len)continue;
-
sum=(sum+m[i])%c;
-
if(sum==0){
-
pos=0;len=i+1;
-
}
-
else if(mod[sum]>=0){
-
pos=mod[sum]+1;
-
len=i-mod[sum];
-
}
-
else
-
mod[sum]=i;
-
}
-
printf("%d",pos+1);
-
for(i=pos+1;i<len+pos;i++)
-
printf(" %d",i+1);
-
printf("/n");
-
}
-
return 0;
-
}