123789456ye

已AFO

[LuoguP2371]墨墨的等式

题面:Luogu
题解:最短路
本来想放张图上来,结果发现\(n=2\)数据太小没用,\(n=3\)到处都是重边,就不放图了
首先我们可以求出\(b\le r\)的数量和\(b\le l-1\)的数量,然后两个相减就是答案
考虑怎么求出符合条件的

\[minnum=\min_{1\le i\le n}a[i] \\ \sum_{i=1}^{n}{a[i]*x[i]}=k \rightarrow\sum_{i=1}^{n}{a[i]*x[i]}=k+x*minnum \]

为什么取最小值?因为这样符合条件的数最多而且跑的最快,因为连边最少
\(dis[i]\)表示\(b\equiv i~(mod~minnum)\)的最小值
于是连有向边\((i,(i+a[j])\%minnum,a[j])\)表示本来余数为\(i\),加上\(a[j]\)后余数改变
然后跑以\(0\)为源点跑最短路(spfa即可,这道题连边方式决定了它卡不了spfa)(当然我还是用的dijskra)
然后如果要求\([0,x]\)间的数量,如果\(dis[i]\le x\)

\[ans+=\lfloor \frac{x-dis[i]}{minnum} \rfloor+1 \]

还是给一组小数据吧

4 5 20
5 6 12 13

\(dis\)值是\(0,6,12,13,19\)(注意是从\(0\)开始编号的)
主要是连边和\(dis\)的意义可以感性理解一下,其它的应该没什么
注意开\(long~long\),以及\(inf\)要开大一点

#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T& x)
{
	x = 0; char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define maxn 500005
#define ll long long
struct Edge
{
	int fr, to, val;
}eg[maxn * 20];
int head[maxn], edgenum;
inline void add(int fr, int to, int val)
{
	eg[++edgenum] = { head[fr],to,val };
	head[fr] = edgenum;
}
#define pa pair<int,int>
#define mp make_pair
priority_queue<pa, vector<pa>, greater<pa> >q;
int vis[maxn], minnum;
ll l, r, dis[maxn];
void dijskra(int s)
{
	for (int i = 0; i < minnum; ++i) dis[i] = 1e18;
	dis[s] = 0; q.push(mp(dis[s], s));
	while (!q.empty())
	{
		int tp = q.top().second; q.pop();
		if (vis[tp]) continue; vis[tp] = 1;
		for (int i = head[tp]; i; i = eg[i].fr)
			if (dis[eg[i].to] > dis[tp] + eg[i].val)
				dis[eg[i].to] = dis[tp] + eg[i].val, q.push(mp(dis[eg[i].to], eg[i].to));
	}
}
inline ll query(ll x)
{
	ll ans = 0;
	for (int i = 0; i < minnum; ++i)
		if (dis[i] <= x) ans += (x - dis[i]) / minnum + 1;
	return ans;
}
int a[maxn], n, m;
int main()
{
	read(n), read(l), read(r);
	minnum = 0x3f3f3f3f;
	for (int i = 1, tp; i <= n; ++i)
	{
		read(tp);
		if (tp) a[++m] = tp, minnum = min(minnum, tp);
	}
	n = m;
	for (int i = 0; i < minnum; ++i)
		for (int j = 1; j <= n; ++j)
			if(a[j] != minnum) add(i, (i + a[j]) % minnum, a[j]);
	dijskra(0);
	printf("%lld\n", query(r) - query(l - 1));
	return 0;
}
posted @ 2020-03-21 22:24  123789456ye  阅读(94)  评论(0编辑  收藏  举报