zoukankan      html  css  js  c++  java
  • [国家集训队]墨墨的等式

    Description

    墨墨突然对等式很感兴趣,他正在研究a1x1+a2y2+…+anxn=B存在非负整数解的条件,他要求你编写一个程序,给定N、{an}、以及B的取值范围,求出有多少B可以使等式存在非负整数解。

    Input

    输入的第一行包含3个正整数,分别表示N、BMin、BMax分别表示数列的长度、B的下界、B的上界。

    输入的第二行包含N个整数,即数列{an}的值。 Output 输出一个整数,表示有多少b可以使等式存在非负整数解。

    Sample Input

    2 5 10

    3 5

    Sample Output

    5

    HINT

    对于100%的数据,N≤12,0≤ai≤5*10^5,1≤BMin≤BMax≤10^12。

    题目可以这样变化一下:n个物品,可以用0-正无穷,问[l,r]区间内有多少价值可以凑出来。

    联系到最短路上面:

    任选一个ai>0,如果一个价值k∗ai+x(0≤x<ai,k≥0)可以被凑出来,那么显然(k+1)∗ai+x,(k+2)∗ai+x,...都可以被凑出来(这样x的范围就是小于ai了)

    显然如果我们对于每个x都找到最小的k满足k∗ai+x可以被凑出来,这个问题就解决了,

    如果满足凑出x的最小花费是大于b的,那么就不能在[l,r]区间内凑出mn*k+x,这个数了,

    否则的话,就计算[l,r]内有多少个可以凑出来。

    最短路,spfa 时间复杂度O(n∗ai∗log2ai) 因为复杂度与ai有关,所以我们就选择最小的ai了,

    举个例子:当最小的ai等于1时,那么自然区间内的所有数都可以凑出来了。

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const ll inf=9e18;
    const int N=5e5+5;
    int q[N],mn,n,a[20];
    ll dis[N];
    bool vis[N];
    void spfa()
    {
        int h=0,w=1,x,y; q[1]=0; vis[0]=1;/*第一个能凑出的数就是0*/ 
        while (h!=w)
        {
            h++; if (h>mn+5) h=1; x=q[h];/*循环队列,取出队头的数*/
            for (int i=1;i<=n;i++)
            {
                y=(x+a[i])%mn;/*利用这个价值和其他价值组合所能达到的y,计算y的最小花费(因为只有计算最小花费),才能用mn凑出更多的满足区间条件的数*/
                if (dis[y]>dis[x]+a[i])
                {
                    dis[y]=dis[x]+a[i];
                    if (!vis[y])
                    {
                        vis[y]=1;
                        w++; if (w>mn+5) w=1; q[w]=y;
                    }
                }
            }
            vis[x]=0;
        }
    }
    
    ll query(ll x)
    {
        ll ans=0;
        for (int i=0;i<mn;i++)
            if (dis[i]<=x) ans+=(x-dis[i])/mn+1; /*计算有多少个k满足k*mn+i<=x,因为k>=0,所以还要加1*/
        return ans;
    }
    
    /*windows 用I64d linux 用lld*/    
    int main()
    {
        mn=(1e9);
        ll L,R;
        scanf("%d%lld%lld",&n,&L,&R);
        for (int i=1;i<=n;i++) { 
            scanf("%d",&a[i]); 
            if (a[i]==0) { 
                i--; n--; 
                continue;
            } 
            mn=min(mn,a[i]);
        }/*取出最小的an,但是不能为0,很好理解吧*/
        for (int i=1;i<mn;i++) dis[i]=inf;/*设达到每个k*mn+i(i<mn)的最小花费,所以数组dis中只有小于mn的i即可(*/
        spfa();
        printf("%lld
    ",query(R)-query(L-1));
        return 0;
    }
  • 相关阅读:
    链表基础操作2
    数据结构第二章课后作业
    CSAPP第二個實驗bomblab
    链表的基础操作1
    Codeforces 375
    Codeforces 372
    Codeforces 367
    线性同余方程组
    【除草】反演
    【转】组合数求模
  • 原文地址:https://www.cnblogs.com/zzrblogs/p/10399061.html
Copyright © 2011-2022 走看看