HYSBZ2118 墨墨的等式(最短路径)
这题是校周赛上碰到的,很经典的一道题,里面的思想很巧妙。
题意:
中文题不解释
要点:
一开始根本想不到是最短路径,我们先把{an}中最小的值找到为a1,这样将所有的an%a1,这样最后的结果就是相当于x+k*a1,那么我们就要求这个x,也就是所有an%a1后组合可以达到的数。可以简单的看出x<a1,这样我们就将0~a1-1建图对应x,边权就是a[i],每次增加边的值后和%a1,最后就转换为一个最短路径问题,用spfa搞定,最后dis[i]就对应x,最后求和就通过(l-dis[i])/a1求出k,两个区间求出的k相减就是区间内部可能有的个数。
#include<iostream>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 500500;
const ll INF = 1000000000000000LL;
ll dis[N];
int a[25],mina,n;
int vis[N];
void spfa()
{
queue<int> q;
memset(vis, 0, sizeof(vis));
for (int i = 0; i <= mina; i++)
dis[i] = INF;
int S = 0, T = mina;
q.push(S); vis[S] = 1; dis[S] = 0;
while (!q.empty())
{
int u = q.front(); q.pop();
vis[u] = 0;
for (int i = 0; i < n; i++)
{
int v = (u + a[i]) % mina;
if (dis[v] > dis[u] + a[i])
{
dis[v] = dis[u] + a[i];
if (!vis[v])
{
q.push(v);
vis[v] = 1;
}
}
}
}
}
int main()
{
ll l, r;
scanf("%d%lld%lld", &n, &l, &r);
mina = N;
for (int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
mina = min(a[i], mina);
}
spfa();
ll x, y, ans = 0;
for (int i = 0; i < mina; i++)
if (dis[i] <= r)
{
x = max(0LL, (l - dis[i]) / mina);
y = (r - dis[i]) / mina;
if (x*mina + dis[i] < l) x++;
if (y*mina + dis[i] > r) y--;
ans += y - x + 1;
}
printf("%lld\n", ans);
return 0;
}