BZOj 墨墨的等式(转化为最短路)题解

题意:中文题意不解释...

思路:这道题居然可以转化为最短路orz,要等式有非负整数解,我们可以转化一下:每个ai不限数量,问你能用ai数组拼出多少个Bmin~Bmax范围内的数,有点像完全背包的感觉,看怎样组合能拼出范围内的数。

我们找出ai中不为零的最小数记为p,如果我们把每个数进行操作ai%p ,那么所有的ai我们都可以用整数倍的p加上它的取模表示了。我们用dis[i]表示如果有一个数x:x%p == i,那么dis储存最小的x,也就是说dis储存着我们能用ai数组拼出的取模p等于i的最小的数,那么dis+n*p我们也能拼出。然后问题就变成了求出dis[i]的最小值,用最短路解决。

代码:

#include<cstdio>
#include<set>
#include<vector>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 500000+5;
const int INF = 0x3f3f3f3f;
bool vis[maxn];
ll dis[maxn];
int a[15];
int mod,n;
void spfa(int start){
    memset(vis,false,sizeof(vis));
    memset(dis,INF,sizeof(dis));
    vis[start] = true;
    dis[start] = 0;
    queue<int> q;
    while(!q.empty()) q.pop();
    q.push(start);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = 1;i <= n;i++){
            int w = a[i];
            int v = (u + w) % mod;
            if(dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                if(!vis[v]){
                    q.push(v);
                    vis[v] = true;
                }
            }
        }
    }
}
ll query(ll x){
    ll ans = 0;
    for(int i = 0;i < mod;i++){
        if(dis[i] <= x)
            ans += (x - dis[i]) / mod + 1;  //k*mod + dis == x
    }
    return ans;
}
int main(){
    ll Bmx,Bmn;
    scanf("%d%lld%lld",&n,&Bmn,&Bmx);
    mod = INF;
    for(int i = 1;i <= n;i++){
        scanf("%d",&a[i]);
        if(a[i] == 0){
            i--,n--;    //为0删除
        }
        mod = min(mod,a[i]);
    }
    spfa(0);
    printf("%lld
",query(Bmx) - query(Bmn - 1));
    return 0;
}
原文地址:https://www.cnblogs.com/KirinSB/p/9413266.html