传送门
\[\texttt\colorbox{lightgreen}{\color{green}passed}\;on\;2021.10.10 \] \[\texttt\colorbox{darkblue}{\color{yellow}rewritten}\;on\;2021.10.15 \]本来这一篇blog
里已经上过这道题的代码了
但是想了想 还是觉得应该再写一篇(写下思路以备不时之需
\[\mathbf{\huge\color{gold}{1 : 上 思 路}} \]
首先看题目上讲:
\[\sum_{i=1}^{n}a_ix_i=b \]用辟谷冷静思考了一会……
这不就是\(\mathbf{\small{\color{gold}{跳楼机}}}\)加强版吗(
所以上同余最短路套路,区别是把一次建俩边换成用一个for
来建边
并且在建完边之后sort
一下来压空间复杂度
最后统计答案的时候要用上确界减掉下确界
\[left=max\{\frac{(dist[i]-i)}{a[1]},\frac{(l-i-1)}{a[1]}+1\} \]
这一句确定下确界,如果 \(dist[i]\) 在左端点 \(l\) 以下的话直接同步成 \(l\).
\(max\) 中第一个值不用+1的原因是下端点不一定取.
这一句是上确界,求出对于 \([1,r]\) 的能取的 \(ans\).
\[\mathbf{\huge\color{gold}{2 : 上 代 码}} \]
#include <bits/stdc++.h>
#define MAXN 500005
#define int long long
using namespace std;
int n,l,r,cntr,ans,a[13],head[MAXN];
int dist[MAXN];
bool vis[MAXN];
struct edge
{
int to,next,w;
}e[MAXN*10];
inline void add(int u,int v,int w)
{
e[++cntr].to=v;
e[cntr].w=w;
e[cntr].next=head[u];
head[u]=cntr;
}
inline void SPFA()
{
queue<int> q;
dist[0]=0;
vis[0]=1;
q.push(0);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to,w=e[i].w;
if(dist[v]>dist[u]+w)
{
dist[v]=dist[u]+w;
if(!vis[v])
vis[v]=1,q.push(v);
}
}
}
}
signed main()
{
scanf("%lld %lld %lld",&n,&l,&r);
int cnt=0;
for(int i=1;i<=n;i++)
{
int input;
scanf("%lld",&input);
if(input) a[++cnt]=input;
if(a[cnt]==1)
{
printf("%lld",r-l+1);
return 0;
}
}
memset(dist,0x3f,sizeof(dist));
sort(a+1,a+cnt+1);
for(int i=0;i<a[1];i++)
for(int j=2;j<=cnt;j++)
add(i,(i+a[j])%a[1],a[j]);
SPFA();
int left,right;
for(int i=0;i<a[1];i++)
{
if(dist[i]>r)
continue;
left=max((dist[i]-i)/a[1],(l-i-1)/a[1]+1);//下确界
right=(r-i)/a[1];
ans+=right-left+1;
}
printf("%lld",ans);
return 0;
}