bzoj 2118 墨墨的等式

来源:互联网 发布:手机紫外线灯软件 编辑:程序博客网 时间:2024/04/30 03:16
又是好一道数论题!令mn为a[1]~a[n]中数的最小值。很显然,如果x能被凑出来,x+mn也能被凑出来。所以我们只需要知道对于每一个x属于[0,mn),满足y%mn==x中最小的y,那么就能知道[1,R]中模mn等于x的数里能凑出来的个数。
注意spfa的时候正无穷要大一点
需要特殊处理一下a=0的情况,但是数据好像a!=0

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>

#define md
#define ll long long
#define inf 1000000000000000LL
#define eps 1e-8
#define N 500010
using namespace std;
int q[N];
ll dis[N];
bool vis[N];
int mn,n;
int a[20];
void spfa()
{
int h=0,w=1,x,y; q[1]=0; vis[0]=1;
while (h!=w)
{
h++; if (h>mn+5) h=1; x=q[h];
for (int i=1;i<=n;i++)
{
y=(x+a[i])%mn;
if (dis[y]>dis[x]+a[i])
{
dis[y]=dis[x]+a[i];
if (!vis[y])
{
vis[y]=1;
w++; if (w>mn+5) w=1; q[w]=y;
}
}
}
vis[x]=0;
}
}

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=(1e9);
ll L,R;
scanf("%d%lld%lld",&n,&L,&R);
for (int i=1;i<=n;i++) { scanf("%d",&a[i]); if (a[i]==0) { i--; n--; continue;} mn=min(mn,a[i]);}
for (int i=1;i<mn;i++) dis[i]=inf;
spfa();
printf("%lld\n",query(R)-query(L-1));
return 0;
}



0 0
原创粉丝点击