抽屉(鸽巢)定理小结

抽屉定理: “把多于kn个东西任意分放进n个空抽屉(k是正整数),那么一定有一个抽屉中放进了至少k+1个东西。”

虽然这是废话,但是应用起来就相当不容易。



poj 2356 Find  a multiple

题意: 给你N个数,从N个数中选出M个数使得这M个数的和是N的倍数,输出M以及这M个数。

思路: 首先求出Sum[1~n] mod N,Sum[i]为前i项和对N取余,如果Sum[i]==0 则前i个数即为所求。如果没有则根据mod的原理,所有的数都在0~N-1之间,而且没有0,则所有的结果都在1~N-1之间。这就好比有N个数,其结果有N-1种,则必有两个结果相等。也就说明必有两个数mod N相等,则这两个数相减的值必是N的倍数。

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
using namespace std;
struct node
{
    int id,sum,x;
};
int cmp(node a,node b)
{
    if(a.sum==b.sum)
    {
        if(a.id<b.id) return 1;
        return 0;
    }
    if(a.sum<b.sum) return 1;
    return 0;
}
int main()
{
    int n;
    while(cin>>n)
    {
        int i,j;
        node a[12345];
        int y[12345];
        memset(a,0,sizeof(a));
        for(i=1; i<=n; i++)
        {
            scanf("%d",&a[i].x);
            y[i]=a[i].x;
            a[i].id=i;
            a[i].sum=a[i-1].sum+a[i].x;
        }
        for(i=1; i<=n; i++)
        {
            a[i].sum%=n;
            if(a[i].sum==0)
            {
                printf("%d\n",a[i].id);
                for(j=1; j<=a[i].id; j++) printf("%d\n",a[j].x);
                break;
            }
        }
        if(i!=n+1) continue;
        sort(a+1,a+1+n,cmp);

        for(i=1;i<n;i++)
        {
            if(a[i].sum==a[i+1].sum)
            {
                printf("%d\n",a[i+1].id-a[i].id);
                for(j=a[i].id+1;j<=a[i+1].id;j++) printf("%d\n",y[j]);
                break;
            }
        }
    }
}

poj 3370  Halloween treats 

同上类似,就不说了,注意输出的是下标而不是具体的数。


poj 3145 Harmony Forever

题意:给你一个集合S,初始是空集。有两种操作,B X 代表把X元素加入到S集合之中,A M,询问集合S中mod M 最小的元素的下标,没有的话输出-1,如果同样小输出最后加进来的那个数的下标。

思路:根据抽屉定理,余数一定在0~M-1之间。这样的话我们把范围进行区间划分为(0,,M-1),(M,2*M-1)...依次类推,这样找出每个区间内的最小值,而这些最小值的最小值就是所求的答案。

这里的区间查找可以使用线段树来维护,但是当M<=5000时,爆搜的速度会比线段树来的快!

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#define inf 999999999
using namespace std;
int ans[40002];
struct tree
{
    int l,r;
    int m,id;
} a[500002*4];
void build(int k,int l,int r)
{
    a[k].l=l;
    a[k].r=r;
    a[k].m=inf;
    a[k].id=0;
    if(a[k].l==a[k].r) return;
    build(k*2,l,(r+l)/2);
    build(k*2+1,(r+l)/2+1,r);
}
void add(int k,int x,int y,int z)
{
    if(a[k].l==x&&a[k].r==x)
    {
        a[k].m=y;
        a[k].id=z;
        return;
    }
    int mid=(a[k].l+a[k].r)/2;
    if(x<=mid) add(k*2,x,y,z);
    else add(k*2+1,x,y,z);
    if(a[k*2].m<a[k*2+1].m)
    {
        a[k].m=a[k*2].m;
        a[k].id=a[k*2].id;
    }
    else
    {
        a[k].m=a[k*2+1].m;
        a[k].id=a[k*2+1].id;
    }
}
tree query(int k,int l,int r)
{
    if(a[k].m==inf) return a[k];
    if(a[k].l==l&&a[k].r==r) return a[k];
    int mid=(a[k].l+a[k].r)/2;;
    if(r<=mid) return query(k*2,l,r);
    else if(l>mid) return query(k*2+1,l,r);
    else
    {
        tree x=query(k*2,l,mid);
        tree y=query(k*2+1,mid+1,r);
        if(x.m<y.m) return x;
        else return y;
    }
}
int main()
{
    int t,cas=1;
    while(scanf("%d",&t),t)
    {
        int cont=1;
        int MAX=-1;
        build(1,0,500002);
        if(cas!=1) puts("");
        printf("Case %d:\n",cas++);
        while(t--)
        {
            char x[12];
            scanf("%s",x);
            if(x[0]=='B')
            {
                int y;
                scanf("%d",&y);
                if(y>MAX) MAX=y;
                add(1,y,y,cont);
                ans[cont++]=y;
            }
            else
            {
                int m;
                scanf("%d",&m);
                if(MAX==-1)
                {
                    puts("-1");
                    continue;
                }
                if(m<=5000)
                {
                    int Min=inf;
                    int mini;
                    for(int i=cont-1; i>=1; i--)
                    {
                        if(ans[i]%m<Min)
                        {
                            Min=ans[i]%m;
                            mini=i;
                        }
                        if(Min==0) break;
                    }
                    printf("%d\n",mini);
                }
                else
                {
                    int Min=inf;
                    int mini=-1;
                    int l,r;
                    l=0;
                    r=m-1;
                    while(l<=MAX)
                    {
                        if(r>MAX) r=MAX;
                        tree cur=query(1,l,r);
                        if(cur.m==inf)   //这个条件不加无限WA...
                        {
                            l+=m;
                            r+=m;
                            continue;
                        }
                        if(cur.m%m<Min)
                        {
                            Min=cur.m%m;
                            mini=cur.id;
                        }
                        else if(cur.m%m==Min)
                        {
                            if(cur.id>mini) mini=cur.id;
                        }
                        l+=m;
                        r+=m;
                    }
                    printf("%d\n",mini);
                }
            }
        }
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值