【BZOJ2118】墨墨的等式(dijkstra)

墨墨的等式传送门


这道题很神奇。

找余数

如果 x 可以为负数,那就变成了拓展欧几里得,但 x 是非负整数,解决方法就不一样了。我们找到所有 a 中最小的非负 a,记做 mn。如果能凑成 mn*x+k (k<a),那么就能凑成 mn*(x+1)+k,也就是可以凑成 B~mn*x+k 之间所有与 mn*x+k 相差 mn 的数,所以我们就要找到对于每一个 k ( 1 <= k < mn ),最小的 mn*x+k 是多少。

dijkstra

记 dis[k] 为最小的 mn*x+k,我们发现如果 mn* x+k_1+a_i = mn*y+k_2 ,那么我们就可以用 dis[k_1] 更新 dis[k_2],即设 t = (k+a_i)%mn, f[t]=min( f[t], f[k]+a_i ),这很像求最短路,因此我们抽象地建一个图,跑 dijkstra。这是在用形象的图论解决抽象的数学问题,非常强。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL;
const int N=15, M=6000000, A=500010, INF1=1000000000;
const LL INF2=1000000000000000;
int n, mi, mn;
LL Bmn, Bmx;
LL dis[A];
int head[A], a[N];
bool vis[N];

struct Edge{
	int to,next,w;
}e[M<<1];

inline void add(int u,int v,int w){
	e[++mi] = (Edge){v,head[u],w};
	head[u] = mi;
}

struct HeadNode{
	LL w; int u;
	bool operator < (const HeadNode & rhs) const{
		return w>rhs.w;
	}
};

priority_queue <HeadNode> Q;

inline void dijkstra(){
	for(int i=1; i<mn; ++i) dis[i]=INF2;
	Q.push((HeadNode){0,0}); dis[0]=0;
	int u, v, p;
	while(!Q.empty()){
		HeadNode c = Q.top(); Q.pop();
		u = c.u;
		if(c.w!=dis[u]) continue;
		for(p=head[u]; p; p=e[p].next){
			v = e[p].to;
			if(dis[v]>dis[u]+e[p].w){
				dis[v] = dis[u]+e[p].w;
				Q.push((HeadNode){dis[v],v});
			}
		}
	}
}

inline LL query(LL x){
	LL ans=0;
	for(int i=0; i<mn; ++i) if(dis[i]<=x){
		ans += (x-dis[i])/mn+1;
	}
	return ans;
}

int main(){
	mn = INF1;
	scanf("%d%lld%lld",&n,&Bmn,&Bmx);
	for(int i=1; i<=n; ++i){
		scanf("%d",&a[i]);
		if(!a[i]){ n--; i--; continue; }
		mn = min(mn,a[i]);			// 找最小的 a  
	}
	for(int i=1; i<=n; ++i) if(a[i]%mn) vis[i]=true;
	for(int i=0, j; i<mn; ++i){
		for(j=1; j<=n; ++j) if(vis[j]){
			add(i,(i+a[j])%mn,a[j]);	// 建图  
		}
	}
	dijkstra();
	printf("%lld\n",query(Bmx)-query(Bmn-1));
	while(1);
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章