BZOJ2118 墨墨的等式

Description

传送门

大致就是给定等式\(a_{1}x_{1} + a_{2}x_{2} + a_{3}x_{3} \dots + a_{n}x_{n}= B\), 现在给定\(\{a\}\),\(B\)的取值范围\([B_{Min}, B_{Max}]\), 求B的取值范围内有多少个B使\(\{x\}\)有非负整数解.

Solution

先进行一个简单的转化, 直接求\([0, l - 1]\)\([0,r]\) 的解然后减去.就只要考虑怎么求\([0, a]\)的答案.

我们进行一个简单的转化, 现在给定n个物品, 每个物品有无数个,求能表示出的价值的个数.

考虑一个数Z作为价值, 如果\(l\)能被Z表示为\(l = kZ + D\), 那么\((l + z)\)也能被表示为\((k + 1)Z + D\)

可以简单的发现\(D \leq Z\) 于是我们考虑答案按照模\(k\)的剩余系分类, 然后最后计算剩余系的总和.

\(a_i\)为$a_i = Min{Q}, Q~mod~k = i $且能被构造.那么答案就很好计算.

我们考虑\(k\)怎么取值.显然,k取\(min \{a_i\}\)最好, 因为它的剩余系大小最小.

那么对于任意一个i和a[j], 我们用最短路求解, 从\(i\)\((a_j + i) \% k\),连接一条长度为\(a_{j}\)的边.

然后跑一遍最短路即可

Codes
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define Debug(s) debug("The massage in line %d, Function %s: %s\n", __LINE__, __FUNCTION__, s)
typedef long long LL;
typedef long double LD;
const int BUF_SIZE = (int)1e6 + 10;
struct fastIO {
    char buf[BUF_SIZE], buf1[BUF_SIZE];
    int cur, cur1;
    FILE *in, *out;
    fastIO() {
        cur = BUF_SIZE, in = stdin, out = stdout;
        cur1 = 0;
    }
    inline char getchar() {
        if(cur == BUF_SIZE) fread(buf, BUF_SIZE, 1, in), cur = 0;
        return *(buf + (cur++));
    }
    inline void putchar(char ch) {
        *(buf1 + (cur1++)) = ch;
        if (cur1 == BUF_SIZE) fwrite(buf1, BUF_SIZE, 1, out), cur1 = 0;
    }
    inline void flush() {
        if (cur1 > 0) fwrite(buf1, cur1, 1, out);
        cur1 = 0;
    }
}IO;
LL read() {
    char ch = IO.getchar();
    LL x = 0, flag = 1;
    for(;!isdigit(ch); ch = IO.getchar()) if(ch == '-') flag *= -1;
    for(;isdigit(ch); ch = IO.getchar()) x = x * 10 + ch - 48;
    return x * flag;
}
void write(LL x) {
    if(x < 0) x = -x, IO.putchar('-');
    if(x >= 10) write(x / 10);
    IO.putchar(x % 10 + 48);
}

#define Maxn 100
#define Maxc 1000009
LL n, l, r, a[Maxn];
struct edge {
    int to, nxt, w;
}g[Maxc * 20];
int head[Maxc], e;
LL Min = LLONG_MAX;
void add(int u, int v, int w) {
    g[++e] = (edge){v, head[u], w}, head[u] = e;
}
namespace SSSP {
    struct node {
        LL id, d;
        int operator < (const node b) const {
            return d > b.d;
        }
    };
    LL dis[Maxc]; int vis[Maxc];
    priority_queue <node> que;
    void Init_Graph() {
        clar(head, -1);
        rep(i, 0, Min - 1)
            rep(j, 1, n) add(i, (1ll * i + a[j]) % Min, a[j]);
    }
    void Dijkstra() {
        clar(dis, 0x3f);
        que.push((node){0, 0}); dis[0] = 0;
        while(!que.empty()) {
            node u = que.top(); que.pop();
            if(vis[u.id]) continue;
            vis[u.id] = 1;
            for(int i = *(head + u.id); ~i; i = g[i].nxt) {
                int v = g[i].to; 
                if(!vis[v] && dis[v] > dis[u.id] + g[i].w) {
                    dis[v] = dis[u.id] + g[i].w;
                    que.push({v, dis[v]});
                }
            }
        }
    }
}
LL solve(LL a) {
    LL ret = 0; 
    rep(i, 0, Min - 1)
        if(SSSP :: dis[i] <= a) 
            ret += (a - SSSP :: dis[i]) / Min + 1;
    return ret;
}
int main() {
#ifdef Qrsikno
    freopen("BZOJ2118.in", "r", stdin);
    freopen("BZOJ2118.out", "w", stdout);
#endif
    n = read(), l = read(), r = read();
    rep(i, 1, n) a[i] = read(), Min = min(a[i], Min);
    SSSP :: Init_Graph();
    SSSP :: Dijkstra();
    write(solve(r) - solve(l - 1)); 
    IO.flush();
    return 0;
}
启发

对于一种求最小化\(a_i\)的值的问题,并且\(a_i\)可以由其他项目简单的动态的求出.可以考虑最短路求解.这事实上提供了一种建模的好方法.

如果\(a_i\) 的取值范围过于大, 可以考虑按模数分类后计算, 统计答案时求和

转载于:https://www.cnblogs.com/qrsikno/p/9791384.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值