【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;
}



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