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;
}


posted @ 2016-12-06 09:20  seasonal  阅读(103)  评论(0编辑  收藏  举报