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