我真不想写背景

69 篇文章 0 订阅

我真不想写背景


题目描述

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


输入格式

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


输出格式

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


样例输入

2 5 10
3 5


样例输出

5


样例解释

对于 B=5,式子有 x1=0 , x2=1
对于 B=6 ,式子有 x1=2,x2=0
对于 B=7 ,无解。
对于 B=8 ,式子有 x1=1 , x2=1
对于 B=9 ,式子有 x1=3 , x2=0
对于 B=10 ,式子有 x1=0 , x2=2


数据范围

20% 的数据, N5 1BMinBMax10
40% 的数据, N10 1BMinBMax106
100% 的数据, N12 0ai4105 1BMinBMax1012


Solution

因为 B 的范围很大,所以我们考虑将其取模。
对于模 a1 的完全剩余系中的每一个元素 y ,若能找到最小的能够表示为

m=i=2naixi

m mod a1=y
m ,则对于范围内的任意一个与 y 关于模 a1 同余的 B 都满足
B=ka1+m

则对于每一个 y ,可以算出范围内的与 y 关于模 a1 同余的 B 的个数为
bmaxma1Max(bminma1,0)+1

那么,如何求出 m 呢?
my(moda1)

my=ka1

i=2naixika1=y

现已知 y1 ,枚举 ak ,则可以更新 (y1+ak) mod a1
fi 表示 i 所对应的 m 要变成 i 需要减去多少个 a1
f(y1+ak) mod a1=fy1+((y1+ak)((y1+ak) mod a1)a1)

这就是一个最短路模型,每次用 SPFA 更新当前元素所能更新的元素。


Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>

#define LL long long
#define Max(x,y) ((x)>(y)?(x):(y))

using namespace std;

LL n,bmi,bma,ans;
LL a[20],dis[400010];
bool in_stack[400010];
queue<LL>q;

void spfa(){
    memset(dis,-1,sizeof dis);
    dis[0]=0;q.push(0);in_stack[0]=true;
    while(!q.empty()){
        LL now=q.front();
        q.pop();in_stack[now]=false;
        for(LL i=2;i<=n;i++){
            LL tmp=now+a[i],qq=0;
            while(tmp>a[1]){
                tmp-=a[1];
                qq++;
            }
            if(dis[tmp]==-1||dis[tmp]>dis[now]+qq){
                dis[tmp]=dis[now]+qq;
                if(!in_stack[tmp]){
                    in_stack[tmp]=true;
                    q.push(tmp);
                }
            }
        }
    }
}

int main(){

    freopen("wth.in","r",stdin);
    freopen("wth.out","w",stdout);

    scanf("%lld%lld%lld",&n,&bmi,&bma);
    for(LL i=1;i<=n;i++)scanf("%lld",&a[i]);

    sort(a+1,a+n+1);

    spfa();
    for(LL i=0;i<a[1];i++)if(dis[i]!=-1){
        LL tmp=i+dis[i]*a[1];
        if(tmp>bma)continue;
        ans+=(bma-tmp)/a[1]-Max((LL)(ceil(((double)bmi-tmp)/a[1])),0)+1;
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值