题解 P2371 【[国家集训队]墨墨的等式】
首先,本题我们发现B很大(≤10的12次方),N很小(≤12)。
那么,我们可以暴力吗?
好像只有20分的样子啊...
...
...
所以我们要想优化(废话)
对于40%的数据,
我们想到背包。
这不是完全背包裸题吗!
所以我们用O(Bn)的复杂度拿到了40分。
... ...
对于100%的数据:
我们对a[]数组找到一个最小值,把它存入mn (srf很懒,排了个序QAQ)
可以发现,如果式子的值可以达到 k * mn + t
那么这个式子的值也一定可以达到 (k + i) * mn + t.
因此,我们可以对每个余数t建一个点,(0 ≤ t ≤ mn)
在 t 和 (t + a[i]) % mn 之间连一条有向边
用SPFA求一下从0开始的最短路。
求得最短路后,我们对于每个余数统计答案即可。
代码:
#include <bits/stdc++.h>
#define N 15
#define M 500005
#define LL long long
using namespace std;
LL INF = (LL)1 << 62; //INF开大!!!
LL n,l,r,a[N];
LL k,dis[M];
LL ans,ll,rr,t1,t2;
queue<int>Q; int x,now;
bool vis[M];
int main(){
ios::sync_with_stdio(false); //卡常专用
cin >> n >> l >> r;
for (int i = 1; i <= n; ++i) cin >> a[i];
sort(a+1,a+n+1);
k = a[1];
//SPFA
dis[0] = 0;
for (int i = 1; i < k; ++i) dis[i] = INF;
while (!Q.empty()) Q.pop();
Q.push(0);
memset(vis,0,sizeof(vis));
vis[0] = 1;
while (!Q.empty()){
x = Q.front(); Q.pop();
for (int i = 1; i <= n; ++i){
now = (x + a[i]) % k;
if (dis[now] > dis[x] + a[i]){
dis[now] = dis[x] + a[i];
if (!vis[now]) Q.push(now);
vis[now] = 1;
}
}
vis[x] = 0;
}
ans = 0;
for (int i = 0; i < k; ++i)
if (dis[i] != INF) dis[i] -= i,dis[i] /= k;
//统计ans
for (int i = 0; i < k; ++i)
if (dis[i] != INF){
ll = (l - 1 - i) / k;
rr = (r - i) / k;
t1 = ll - dis[i] + 1;
t2 = rr - dis[i] + 1;
t1 = max((LL)0,t1);
t2 = max((LL)0,t2);
ans = ans - t1 + t2;
}
//收工
cout << ans << endl;
return 0;
}