把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷2371】[国家集训队] 墨墨的等式(同余最短路)

点此看题面

  • 给定一个长度为\(n\)的序列\(a_{1\sim n}\),求有多少\(b\in[l,r]\),使得\(\sum_{i=1}^na_ix_i=b\)存在一组非负整数解。
  • \(n\le12,a_i\le5\times10^5\)

余数分类+最短路建图求解

刚看到\(n\le12\)这种数据范围自然而然以为是乱搞,结果想了半天无果,最后才发现这竟是一道正经题目。。。

假设\(a_i\)的最小值为\(Mn\),容易发现,如果我们能表示出\(k\times Mn+r\),就必然能表示出\((k+1)\times Mn+r\)

换言之,只要对于模\(Mn\)的每种余数\(r\),求出能够表示出的\(k\times Mn+r\)的最小的\(k\)即可。

而要解决这种问题,一种套路的做法就是建图。

考虑枚举每种余数\(r\),我们可以任选一个\(a_i\)给它加上,因此连一条\(r\rightarrow (r+a_i)\% Mn\)的边权为\(\lfloor\frac{r+a_i}{Mn}\rfloor\)的边。

那么从\(0\)开始到每个点的最短路就是每个\(r\)对应的最小的\(k\)了。

代码:\(O(nV+VlogV)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 12
#define V 500000
#define LL long long
#define Pr pair<int,int>
#define mp make_pair
#define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
using namespace std;
int n,a[N+5],ee,lnk[V+5];struct edge {int to,nxt,v;}e[N*V+5];
int dis[V+5],vis[V+5];priority_queue<Pr,vector<Pr>,greater<Pr> > q;I void Dij()//Dijkstra
{
	RI i;Pr k;for(i=1;i^a[1];++i) dis[i]=1e9;q.push(mp(0,0));//初始从0出发
	W(!q.empty()) if(k=q.top(),q.pop(),!vis[k.second]) for(vis[k.second]=1,i=lnk[k.second];
		i;i=e[i].nxt) k.first+e[i].v<dis[e[i].to]&&(q.push(mp(dis[e[i].to]=k.first+e[i].v,e[i].to)),0);
}
int main()
{
	RI i,j;LL l,r;for(scanf("%d%lld%lld",&n,&l,&r),i=1;i<=n;++i) scanf("%d",a+i);
	for(sort(a+1,a+n+1),i=2;i<=n;++i) for(j=0;j^a[1];++j) add(j,(j+a[i])%a[1],(j+a[i])/a[1]);//枚举余数和加上的数建图
	LL t=0;for(Dij(),i=0;i^a[1];++i) t+=max((r-i)/a[1]-max(dis[i]-1LL,(l-1-i)/a[1]),0LL);//枚举每种余数统计答案
	return printf("%lld\n",t),0;
}
posted @ 2021-03-25 13:49  TheLostWeak  阅读(24)  评论(0编辑  收藏  举报