http://www.lydsy.com/JudgeOnline/problem.php?id=2118 (题目链接)
题意
给出B的取值范围[Bmin,Bmax],求方程a1x1+a2x2+…+anxn=B有多少B可以使等式存在非负整数解。
Solution
问题很容易就被转化为:用a1,a2,a3,······an能组成多少个在范围[Bmin,Bmax]内的数。这是一类经典的图论问题。
我们假设a[]中最小的元素为T,可以考虑用n个数能够组成的数对T的模的情况。用dis[i]表示构成的一个数Q,且Q mod T=i,Q是满足上述两个条件的最小值。我们在这里将题目中的区间改为具体的询问,更好的进行讨论,对于询问X,设X mod T=i,则有以下三种情况:
- dis[i]>x。由于用这n个数构成的一个模T为i的数,这个数的最小值为dis[i],而dis[i]>x,说明X是无法构成的。
- dis[i]=x。由于用这n个数构成的一个模T为i的数,这个数的最小值为dis[i],而dis[i]=x,说明X可以构成,且是能构成的模T等于i的最小的数。
- dis[i]<x。由于用这n个数构成的一个比X更小的 mod T为i的数Q,则X mod T=Q mod T,且X必定可以由Q加上若干个T得到,因此,X也是可以构成的。
由上述三点可知,当dis[i]<=X时,X是可以被构成的,否则则不能。
现在的问题是如何求解dis数组?
相信各位看官已经发现dis数组的命名有点诡异,没错就是用最短路求解。由于dis[i] mod T=i,i的范围在0~T-1内,因此可以建立T个点0,1,2,······,T-1。对于点i和任意一个数a[j],设k=(i+a[j]) mod T,可以认为从i到k连条边权为a[j]的边,表示可以从 mod T=i这个点,通过加上边权a[j],到达 mod T=k的点。由于T mod T=0,即可设T为数字编号为0的点。要求X是否能由n个数构成,就要求出dis[X mod T]的最小值了;当X大于等于dis[X mod T],它就能够由着n个数构成,设X mod T=j,dis[j]即为j这个店到达0点的最短距离,它可以由0点直接加边权a[j]得到,也可以经过其他中间点到达。转换后,它就是个最短路问题了。
再回到这个问题上。于是我们先建图,跑一遍最短路,预处理出dis数组,然后枚举i=0~T-1,计算模T为i的数在区间[Bmin,Bmax]中有多少个,统计答案即可。
细节
堆里面又忘记开long long了,尴尬。
代码
// bzoj2118 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> #define LL long long #define MOD 10007 #define inf (1ll<<60) #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; const int maxn=500010; struct edge {int to,next,w;}e[maxn*10]; struct data { LL num,w; friend bool operator < (const data a,const data b) { return a.w>b.w; } }; int head[maxn],a[maxn],vis[maxn]; int n,cnt; LL L,dis[maxn],R; void link(int u,int v,int w) { e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].w=w; } void Dijkstra() { priority_queue<data> q; for (int i=0;i<a[1];i++) dis[i]=inf; data x=(data){0,0},y; dis[0]=0; q.push(x); while (!q.empty()) { x=q.top();q.pop(); if (vis[x.num]) continue; vis[x.num]=1; for (int i=head[x.num];i;i=e[i].next) if (dis[e[i].to]>x.w+e[i].w) { y.w=dis[e[i].to]=x.w+e[i].w; y.num=e[i].to; q.push(y); } } } int main() { scanf("%d%lld%lld",&n,&L,&R); for (int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n); if (!a[n]) return printf("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]); Dijkstra(); LL ans=0; for (int i=0;i<a[1];i++) if (dis[i]<=R) { LL l=max(0ll,(L-dis[i])/a[1]); if (l*a[1]+dis[i]<L) l++; LL r=(R-dis[i])/a[1]; if (r*a[1]+dis[i]>R) r--; ans+=r-l+1; } printf("%lld\n",ans); return 0; }