这两道题都是鸽巢原理的经典应用。
首先简单说一下鸽巢原理,就是有n+1个球,放在n个盒子中,那么至少有一个盒子有两个或更多的球。
而这两道题正是运用了这个思想。先对数列求前N项和,有s1=a1,s2=a1+a2,
s3=a1+a2+a3,......,sn=a1+a2+a3+...+an。若其中有和可以整除m,那么即可得出答案,否则,所得的余数只会是1,2,...,n-1,因此,必存在至少两个数模除m后有相同余数r,即si=b*m+r,sj=c*m+r,则si-sj=(b-c)*m。则序列ai到aj的和即为所求。
以下是poj 2356的代码:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int M=15010; int hash[M][2]; __int64 sum[M],a[M]; int n; void init() { for(int i=0;i<=n;i++) { hash[i][0]=hash[i][1]=0; } sum[0]=0; } int main() { // freopen("in.txt","r",stdin); while(scanf("%d",&n)==1) { init(); int i; for(i=1;i<=n;i++) { scanf("%I64d",&a[i]); sum[i]=sum[i-1]+a[i]; } bool flag=false; int l,k; for(i=1;i<=n;i++) { int t=sum[i]%n; if(t==0) { l=0; k=i; flag=true; break; } if(hash[t][0]==0) { hash[t][0]=1; hash[t][1]=i; } else { flag=true; l=hash[t][1]; k=i; break; } } if(flag) { printf("%d/n",k-l); for(i=l+1;i<=k;i++) printf("%I64d/n",a[i]); } else printf("0/n"); } return 0; }
接下是poj 3370,只要先排个序就和上题一样了
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=100100;
struct neib
{
int val;
int num;
bool operator < (const neib a) const{
if(val==a.val)
return num<a.num;
else
return val<a.val;
}
}s[M];
int hash[M][2];
__int64 sum[M];
int c,n;
void init()
{
int i;
for(i=0;i<=c+5;i++)
{
hash[i][0]=hash[i][1]=0;
}
sum[0]=0;
}
int main()
{
// freopen("in.txt","r",stdin);
while(scanf("%d%d",&c,&n)==2)
{
if(!c && !n) break;
init();
int i;
for(i=1;i<=n;i++)
{
scanf("%d",&s[i].val);
s[i].num=i;
}
sort(s+1,s+n+1);
for(i=1;i<=n;i++)
sum[i]=sum[i-1]+s[i].val;
bool flag=false;
int l,r;
for(i=1;i<=n;i++)
{
__int64 temp=sum[i]%c;
if(temp==0)
{
l=0;
r=i;
flag=true;
break;
}
else
{
if(hash[temp][0]==0)
{
hash[temp][0]=1;
hash[temp][1]=i;
}
else
{
l=hash[temp][1];
r=i;
flag=true;
break;
}
}
}
if(flag)
{
for(i=l+1;i<=r;i++)
printf("%d ",s[i].num);
}
else
{
printf("no sweets/n");
}
printf("/n");
}
return 0;
}