洛谷P2371 [国家集训队]墨墨的等式

不愧是国家集训队的题目,神仙题啊
题目所让求的实际上是 ( ∑ i = 1 n a i x i ) (\sum_{i=1}^n a_ix_i) (i=1naixi)在所给定的 [ l , r ] [l,r] [l,r]区间内的取值个数
我们令 ( ∑ i = 1 n a i x i ) (\sum_{i=1}^n a_ix_i) (i=1naixi) S S S
我们考虑对于 S S S a i a_i ai的值,假设 S = k ∗ a i + r e s t S=k*a_i+rest S=kai+rest,如果存在方式可以表示出S的话,那么一定存在 S ′ = ( k + 1 ) ∗ a i + r e s t S^{'}=(k+1)*a_i+rest S=(k+1)ai+rest,所以在这种情况下,S提供的贡献就是下式
A N S + = { B m a x − S ) / a i + 1 ; S  ∈ [ B m i n , B m a x ]   ( B m a x − S ) / a i − ( B m i n − S ) / a i S  ∈ [ 0 , B m i n ) ANS += \begin{cases} B_{max}-S)/a_i+1; & \text{S $\in[B_{min},B_{max}]$ } \\ (B_{max}-S)/a_i-(B_{min}-S)/a_i & \text{S $ \in[0,B_{min}) $} \end{cases} ANS+={BmaxS)/ai+1;(BmaxS)/ai(BminS)/ai[Bmin,Bmax] [0,Bmin)

那么接下来我们需要考虑两件事

如何获取S
如何确定 a i a_i ai的值

通过上式不难看出 a i a_i ai是越小越好的,所以取 a a a数组中最小值就好
那么S呢,因为对 a i a_i ai取了模,所以对我们更重要的实际上是能达到 r e s t rest rest的最小值是多少,因为取模后总共只有 a i − 1 a_i-1 ai1种取值,比如膜 a i a_i ai后是 r e s t rest rest,那么 k 1 ∗ a i + r e s t k_1*a_i+rest k1ai+rest k 2 ∗ a i + r e s t k_2*a_i+rest k2ai+rest k 1 &lt; k 2 k_1&lt;k_2 k1<k2的情况下前者一定优于后者,前者对于答案的贡献一定包含后者,所以后者是无用的
所以我们要求的总共只有 a i a_i ai种情况
那么这又如何求出呢,跑一次膜意义下最短路就行了
d i s [ i ] dis[i] dis[i]表示余数为 i i i的最小数,那么连边很显然
对于 a i ( i &gt; = 2 ) a_i(i&gt;=2) ai(i>=2),我们连接 d i s [ i ] dis[i] dis[i] ( d i s [ i ] + a i ) m o d &ThinSpace;&ThinSpace; a 1 (dis[i]+a_i)\mod {a_1} (dis[i]+ai)moda1,边权为 a i a_i ai
完事,开跑

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 5e5+7;
const ll INF = 1e13;
ll mod,dis[maxn],n,bmax,bmin,val[200];
bool cmp1(ll a,ll b){return a<b;}
int vis[maxn];

void spfa(){
	for(int i=0;i<mod;i++)dis[i]=INF;
	dis[0]=0;vis[0]=1;
	queue<ll>q;q.push(0);
	while(!q.empty()){
		ll f1=q.front();q.pop();vis[f1]=0;
		for(int i=2;i<=n;i++){
			ll to=(f1+val[i])%mod;
			if(dis[to]>dis[f1]+val[i]){
				dis[to]=dis[f1]+val[i];
				if(!vis[to])vis[to]=1,q.push(to);
			}
		}
	}
}

int main(){
	scanf("%lld%lld%lld",&n,&bmin,&bmax);bmin--;
	for(ll i=1;i<=n;i++){
		scanf("%lld",&val[i]);
	}
	sort(val+1,val+1+n,cmp1);
	mod=val[1];
	spfa();
	ll ans=0;
	for(int i=0;i<mod;i++){
		if(dis[i]>bmax)continue;
		else if(dis[i]>=bmin&&dis[i]<=bmax){
			ans+=(bmax-dis[i])/mod+1;
		}
		else {
			ans+=(bmax-dis[i])/mod-(bmin-dis[i])/mod;
		}
	}
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值