P2371-[國家集訓隊]墨墨的等式【同餘最短路】

Quant-Ask發表於2020-10-27

正題

題目連結:https://www.luogu.com.cn/problem/P2371


題目大意

n n n a i a_i ai,求有多少個 b ∈ [ l , r ] b\in[l,r] b[l,r]滿足 ∑ i = 1 n a i x i = b \sum_{i=1}^na_ix_i=b i=1naixi=b有正整數解。


解題思路

因為有一個 a 1 a_1 a1在,而且 x 1 x_1 x1可以是任意正整數,所以如果在 b b b很大的情況下,能夠湊出一個數 k k k使得 k % a 1 = b % a 1 k\% a_1=b\% a_1 k%a1=b%a1那就能湊出 b b b

所以我們設 f i f_i fi表示能湊出的最小的 k k k使得 k % a 1 = i k\% a_1=i k%a1=i。然後最短路轉移即可。

時間複雜度 O ( a 1 n ) O(a_1n) O(a1n)


c o d e code code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const ll N=5e5+10;
struct node{
	ll to,next,w;
}a[N*20];
struct point{
	ll x,dis;
};
bool operator<(point x,point y)
{return x.dis>y.dis;}
priority_queue<point> q; 
ll n,m,l,r,tot,f[N],ls[N],w[N];
bool v[N];
void addl(ll x,ll y,ll w){
	a[++tot].to=y;
	a[tot].next=ls[x];
	a[tot].w=w;
	ls[x]=tot;return;
}
void Dij(){
	memset(f,0x3f,sizeof(f));
	f[0]=0;q.push((point){0,0});
	while(!q.empty()){
		ll x=q.top().x;q.pop();
		if(v[x])continue;v[x]=1;
		for(ll i=ls[x];i;i=a[i].next){
			ll y=a[i].to;
			if(f[x]+a[i].w<f[y]){
				f[y]=f[x]+a[i].w;
				if(!v[y])q.push((point){y,f[y]});
			}
		}
	}
	return;
}
ll get(ll x){
	ll ans=0;
	for(ll i=0;i<m;i++)
		if(x>=f[i])ans+=(x-f[i])/m+1;
	return ans;
}
int main()
{
	scanf("%lld%lld%lld",&n,&l,&r);
	for(ll i=1;i<=n;i++)
		scanf("%lld",&w[i]);
	for(ll i=1;i<=n;i++)
		if(!w[i])swap(w[i],w[n]),n--;
	sort(w+1,w+1+n);m=w[1];
	for(ll i=2;i<=n;i++){
		for(ll j=0;j<m;j++)
			addl(j,(j+w[i])%m,w[i]);
	}
	Dij();
	printf("%lld",get(r)-get(l-1));
}

相關文章