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}, Qmodk = 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\) 的取值范围过于大, 可以考虑按模数分类后计算, 统计答案时求和

posted @ 2018-10-15 15:45  Qrsikno  阅读(194)  评论(0编辑  收藏  举报