转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove
最基础的原理便是n+1的物体放到n个盒子里,至少有一个盒子放了两个物体。
poj 2356
http://poj.org/problem?id=2356
有n个数,从中选出几个数的和是n的倍数。
不得不说数学是个神奇的东西,结论是任意的n个数,必然能找到连续的m个数之和是n的倍数。
接下来简单证明一下,组合数学书中,黑书都有介绍。Sk表示a1+a2+……ak,如果Sk是n的倍数,那就直接取Sk了,否则S1-Sn除n的余数分布在1---(n-1)这n-1个数中,运用鸽巢原理,必然有两个的余数相同,即(Si%n)=(Sj%n),即(Sj-Si)%n=0,证毕。
/*
ID:cxlove
PROB:POJ 2356
DATA:2012.4.6
HINT:鸽巢原理
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main(){
int n,k;
int a[10005]={0},b[10005];
while(scanf("%d",&n)!=EOF){
bool flag=false;
memset(b,0,sizeof(b));
for(int i=1;i<=n;i++){
scanf("%d",&k);
if(flag) continue;
a[i]=a[i-1]+k;
if(a[i]%n==0){
printf("%d\n",i);
for(int j=1;j<=i;j++)
printf("%d\n",a[j]-a[j-1]);
flag=true;
}
else if(b[a[i]%n]){
printf("%d\n",i-b[a[i]%n]);
for(int j=b[a[i]%n]+1;j<=i;j++)
printf("%d\n",a[j]-a[j-1]);
flag=true;
}
else
b[a[i]%n]=i;
}
}
return 0;
}
POJ 3370 http://poj.org/problem?id=3370
和上题差不多,从n个数中找出m个数的和是c的倍数,因为c<=n,运用上面的证明,则必然存在连续的k个数是c的倍数,注意下可能会溢出就行了。
/*
ID:cxlove
PROB:POJ 3370
DATA:2012.4.6
HINT:鸽巢原理
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
LL a[100005]={0},b[100005];
int main(){
int n,k,c;
while(scanf("%d%d",&c,&n)!=EOF&&n+c){
bool flag=false;
memset(b,0,sizeof(b));
for(int i=1;i<=n;i++){
scanf("%d",&k);
if(flag) continue;
a[i]=a[i-1]+k;
if(a[i]%c==0){
for(int j=1;j<i;j++)
printf("%d ",j);
printf("%d\n",i);
flag=true;
}
else if(b[a[i]%c]){
for(int j=b[a[i]%c]+1;j<i;j++)
printf("%d ",j);
printf("%d\n",i);
flag=true;
}
else
b[a[i]%c]=i;
}
}
return 0;
}
POJ 3145 http://poj.org/problem?id=3145
以前做的一题,鸽巢原理的巧妙运用。
有两种操作
B X,将数学x(1-500000)放到集合中,
A Y ,输出集合中模Y最小的数。如果有多个,输出最后放入集合的元素。
考虑以下问题,在0-(Y-1)中模Y最小的数一定是0-(Y-1)中数字最小的数,那么依此类推(Y~2*Y-1)(2*Y~3*Y-1)……,线段树处理数字的插入工作,并且查找区间的最小数字。
/*
ID:cxlove
PROB:Harmony Forever
DATA:2012.2.24
HINT:线段树,鸽巢原理
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 1<<30
#define MAX 500000
using namespace std;
struct line{
int left,right,mid;
int val;
}L[MAX*4];
int pos[MAX+5],cnt,val[MAX+5];
void bulid(int step,int l,int r){
L[step].left=l;
L[step].right=r;
L[step].mid=(l+r)/2;
L[step].val=inf;
if(l==r)
return;
bulid(step<<1,l,(l+r)/2);
bulid(step<<1|1,(l+r)/2+1,r);
}
void update(int step,int pos){
if(L[step].left>pos||L[step].right<pos)
return;
if(L[step].left>=pos&&L[step].right<=pos){
L[step].val=pos;
return ;
}
if(pos<=L[step].mid)
update(step<<1,pos);
else
update(step<<1|1,pos);
L[step].val=min(L[step<<1].val,L[step<<1|1].val);
}
int query(int step,int l,int r){
if(L[step].left>r||L[step].right<l)
return inf;
if(L[step].left>=l&&r>=L[step].right)
return L[step].val;
if(L[step].left<L[step].right)
return min(query(step<<1,l,r),query(step<<1|1,l,r));
return inf;
}
void slove(int mod){
int l=0,r=mod-1,ans=-1,temp,k;
while(l<=MAX){
if(r>MAX)
r=MAX;
temp=query(1,l,r);
if(temp!=inf){
if(ans==-1||(temp%mod)<(ans%mod))
ans=temp;
else if((temp%mod)==(ans%mod)&&pos[temp]>pos[ans])
ans=temp;
}
l+=mod;
r+=mod;
}
printf("%d\n",pos[ans]);
}
void fun(int mod){
int ans=inf,k;
for(int i=cnt-1;i>=1;i--){
if(val[i]%mod==0){
k=i;
break;
}
if(val[i]%mod<ans){
ans=val[i]%mod;
k=i;
}
}
printf("%d\n",k);
}
int main(){
int q,m,tt=0;
char str[5];
while(scanf("%d",&q)&&q){
if(tt>0)
printf("\n");
printf("Case %d:\n",++tt);
bulid(1,0,MAX);
cnt=1;
for(int i=0;i<q;i++){
scanf("%s",str);
if(str[0]=='B'){
scanf("%d",&m);
val[cnt]=m;
pos[m]=cnt++;
update(1,m);
}
else{
scanf("%d",&m);
if(cnt==1)
printf("-1\n");
else if(m<=5000)
fun(m);
else
slove(m);
}
}
}
return 0;
}