bzoj 2118 墨墨的等式(好题)

题面

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

输入

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

输出

输出一个整数,表示有多少b可以使等式存在非负整数解。

样例

样例输入

2 5 10
3 5

样例输出

5

解答

我死活都想不到这TM和最短路有关系。大概弱是原罪吧!

$15ptr$做法

爆搜。剪剪枝,其实也没多大用

//
// Created by dhy on 19-2-11.
//
# include <iostream>
using namespace std;
int ans;
int n;
int num[14];
int re_sum[14];
void dfs(int pos,long long sum,long long maxx,long long minn){
    if(pos>n&&sum<=maxx&&sum>=minn){ans++;return; }
    if(sum+re_sum[pos+1]>=maxx){ans++; return;}
    else if(pos>n)return;
    for(int i = 0;i*num[pos]+sum<=maxx;i++){
        dfs(pos+1,sum+i*num[pos],maxx,minn);
    }
}
int main(){
    int bmax,bmin;
    cin>>n>>bmin>>bmax;
    for(int i = 1;i<=n;i++){
        cin>>num[i];
    }
    for(int i = n;i>=1;i--){
        re_sum[i] = num[i]+re_sum[i+1];
    }
    dfs(1,0,bmax,bmin);
    cout<<ans;
    return 0;
}

$100ptr$做法

我们考虑对最后可以凑出来的数进行分类。对这个数进行对$min(a_i)$取模,分成$0-min(a_i)-1$类,对于每一类考虑有哪些可以由这一类拓展过来。如$v$可以被凑出来那么$v+m$(m代指$min(a_i)$)也可以被凑出来。也就是说,只要我们找到一个最小的,在模m意义下等于p($p\in[1,m-1]$)的数,那么我们就可以算出所以在范围内的数。要找出最小的那个数,我们发现这就是最短路模型,由i向i+a[j]%m连边。最后遍历dis[1-m-1]统计一下答案就行。至于存边的数组的大小,玄学,我懒得去算了开大点,不要爆空间就行。 代码:

//
// Created by dhy on 19-2-11.
//
# include <iostream>
# include <queue>
# include <algorithm>
# include <cstring>
using namespace std;
int n;
int num[14];
const int MAXN = (int)5e5+10;
bool viss[MAXN];
long long dis[MAXN];
struct edge{
    long long t,w,next;
}edges[MAXN*20];
int head[MAXN];
int topp;
void add(int f,int t,int w) {
    edges[++topp].next = head[f];
    edges[topp].t = t;
    edges[topp].w = w;
    head[f] = topp;
}
void spfa(){
    memset(dis,0x6f,sizeof(dis));
    memset(viss,false,sizeof(viss));
    queue<long long> q;
    int s = 0;
    viss[s] = true;
    dis[s] = 0;
    q.push(s);
    while(!q.empty()){
        long long top = q.front();
        q.pop();
        viss[top] = false;
        for(long long i = head[top];i!=0;i = edges[i].next){
            long long t = edges[i].t;
            if(dis[t]>dis[top]+edges[i].w){
                dis[t] = dis[top]+edges[i].w;
                if(!viss[t]){
                    viss[t] = true;
                    q.push(t);
                }
            }
        }
    }
}

int main(){
    long long bmax,bmin;
    cin>>n>>bmin>>bmax;
    for(int i = 1;i<=n;i++)cin>>num[i];
    sort(num+1,num+1+n);
    int mn = num[1];
    for(int i = 0;i<mn;i++){
        for(int j = 2;j<=n;j++){
            add(i,(i+num[j])%mn,num[j]);
        }
    }
    spfa();
    long long ans = 0;
    for(int i = 0;i<mn;i++){
        if(dis[i]>bmax)continue;
        if(dis[i]>bmin){
            ans+=(bmax-dis[i])/mn+1;
        } else{
            ans+=(bmax-dis[i])/mn+1;
            ans-=(bmin-dis[i]+mn-1)/mn;
        }
    }
    cout<<ans;
    return 0;
}