BZOJ2118

题解:显然这是一个无限背包的问题

有一个套路

显然关于a进行排序

我们用dis[i]表示用a[2],a[3]...a[n]构成的最小的值%a[1]==i

然后求解问题就很简单了
考虑[l,r]相当于前缀(r)-前缀(l-1)
显然一个前缀很好求
因为构成的和%a[1]意义下只可能是0~a[1]-1
所以枚举每一个0~a[1]-1
如果dis[i]<=solve(x)中的x那么就可行
然后所有dis[i]+k*a[1]均可以
因为dis[i]是可以构成的且是最小的%a[1]==i的情况下的值
所以dis[i]+k*a[1]一定也是可以构成的
所以tot累加一下就行了
又因为%a[1]取值都不同
所以不可能有重复

代码如下:

#include<bits/stdc++.h>
#define ll long long
#define N 6000005
using namespace std;
int n,kk,q[N],head[N],a[N];ll L,R,dis[N];
struct Edge{int nxt,to,step;}e[N];
inline void link(int x,int y,int z){e[++kk].nxt=head[x];e[kk].to=y;e[kk].step=z;head[x]=kk;}
inline void spfa(){
	int left1=1;int right1=1;
	q[left1]=0;memset(dis,127,sizeof(dis));dis[0]=0;
	while (left1<=right1){
		int u=q[left1];
		for (int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if (dis[v]>dis[u]+e[i].step){
				q[++right1]=v;
				dis[v]=dis[u]+e[i].step;
			}
		}
		left1++;
	}
}
inline ll solve(ll x){
	ll tot=0;
	for (int i=0;i<a[1];i++)
		if (dis[i]<=x) tot+=1ll*((x-dis[i])/a[1]+1);
	return tot;
}
int main(){
	scanf("%d%lld%lld",&n,&L,&R);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	if (!a[n]) return puts("0"),0;
	for (int i=0;i<a[1];i++)
		for (int j=2;j<=n;j++) link(i,(a[j]+i)%a[1],a[j]);
	spfa();
	//for (int i=0;i<a[1];i++) printf("WTF%d\n",dis[i]);
	printf("%lld\n",solve(R)-solve(L-1));
	return 0;
}

  

posted @ 2018-05-31 19:21  longint  阅读(94)  评论(0编辑  收藏  举报