同余最短路

看上去就很废……

题意

有一个长度为 \(n\) 的数列 \(a\),我任意造出一个数组 \(b\),记 \(s=\sum a_ib_i\),求 \(s\)\([0,M]\) 中有多少种取值。

解法

发现,如果 \(s\) 可以取到,则 \(s+ka_1\) 也可以取到。

所以我们就先不用 \(a_1\),用其它数拼凑出一个数字 \(x\),那么 \(x+ka_1\) 都可以被取到。

所以在模 \(a_1\) 的情况下,我们需要知道最少要多少才能有数字 \(0,1,2,...,a_1-1\)。也就是说,为了在模意义下 \(=x\),在不模的意义下最小是多少。比如 \(M=100,a_1=10\),如果我能拼出 23 和 93,由于拼出 23 后不断加 \(a_1\) 可以得到 23,33,43……93,所以拼出 93 是没用的。我们只用找到在模 \(a_1\) 意义下,拼出 3 的最小值就可以了。

于是我们就建立 \(0,1,...,a_1-1\) 这些点。我们知道一个 \(a_i\)\(i\neq 1\)),可以让 \(x\) 变成 \((x+a_i) \bmod a_1\)。所以我们枚举除了 1 的每个 \(a_i\),连边 \(j\to (j+a_i)\bmod a_1:a_i\)

然后从 0 开始跑最短路。然后我们就知道了拼出模 \(a_1\) 的情况下的数字最小值 \(dis_i\)

于是答案就是 \(\Large \sum\limits_{i=0}^{a_1-1} (1+\frac{M-dis_i}{a_1})\),下取整。

由于算法,我们肯定会让 \(a_1\)\(a\) 中最小值。时间复杂度为 \(O(\ na_1+(n+a_1)\log (n+a_1)\ )\)( 建图 + dij )。

例题

洛谷,跳楼机

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define per(i,x,y) for(int i=x;i>=y;--i)
#define mar(o) for(int E=fst[o];E;E=e[E].nxt)
#define v e[E].to
#define lon long long
using namespace std;
const int n7=101234,m7=501234;
struct dino{int to,nxt,w;}e[m7];
int a[5],ecnt,fst[n7],u[n7];
lon N,ans,dis[n7];

lon rd(){
	lon shu=0;bool fu=0;char ch=getchar();
	while( !isdigit(ch) ){if(ch=='-')fu=1;ch=getchar();}
	while( isdigit(ch) )shu=(shu<<1)+(shu<<3)+ch-'0',ch=getchar();
	return fu?-shu:shu;
}

void edge(int p,int q,int w){
	ecnt++;
	e[ecnt]=(dino){q,fst[p],w};
	fst[p]=ecnt;
}

void dijk(){
	memset(dis,0x3f,sizeof dis),dis[1]=0;
	priority_queue < pair<lon,int> > que;
	que.push( make_pair(0,1) );
	while( !que.empty() ){
		int o=que.top().second;que.pop();
		if(u[o])continue;
		u[o]=1;
		mar(o){
			if(dis[v]>dis[o]+e[E].w){
				dis[v]=dis[o]+e[E].w;
				if(!u[v])que.push( make_pair(-dis[v],v) );
			}
		}
	}
}	

int main(){
	N=rd()-1,a[1]=rd(),a[2]=rd(),a[3]=rd();
	sort(a+1,a+4);
	rep(i,0,a[1]-1){
		edge(i+1,(i+a[2])%a[1]+1,a[2]);
		edge(i+1,(i+a[3])%a[1]+1,a[3]);
	}
	dijk();
	rep(i,1,a[1])if(dis[i]<=N)ans=ans+(N-dis[i])/a[1]+1;
	printf("%lld",ans);
	return 0;
}
posted @ 2022-02-11 16:04  BlankAo  阅读(36)  评论(0编辑  收藏  举报