转载请注明出处,谢谢 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; }
以前做的一题,鸽巢原理的巧妙运用。
有两种操作
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; }