Bzoj2118 墨墨的等式

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 1488  Solved: 578

Description

墨墨突然对等式很感兴趣,他正在研究a1x1+a2y2+…+anxn=B存在非负整数解的条件,他要求你编写一个程序,给定N、{an}、以及B的取值范围,求出有多少B可以使等式存在非负整数解。

Input

输入的第一行包含3个正整数,分别表示N、BMin、BMax分别表示数列的长度、B的下界、B的上界。输入的第二行包含N个整数,即数列{an}的值。

Output

输出一个整数,表示有多少b可以使等式存在非负整数解。

Sample Input

2 5 10
3 5

Sample Output

5

HINT

 

对于100%的数据,N≤12,0≤ai≤5*10^5,1≤BMin≤BMax≤10^12。

 

Source

 

同余类最短路

在所有读入的a[i]中,找到最小的一个a为基准,在模a的意义下计算问题

假设通过一些数可以凑出x,使得x%a==j,那么通过累加a就可以凑出所有%a==j的大数。

设dis[i]表示能凑到的模a余i的最小数,SPFA算出dis数组,再利用dis计算Bmin~Bmax内可以凑出的数的个数。

 

一:刚开始写了建边的版本,但是因为要连的边太多了,常数爆炸,4000+ms通过

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<queue>
 6 #define LL long long
 7 using namespace std;
 8 const int mxn=500100;
 9 int read(){
10     int x=0,f=1;char ch=getchar();
11     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
13     return x*f;
14 }
15 struct edge{
16     int v,nxt;
17     LL dis;
18 }e[mxn*10];
19 int hd[mxn],mct=0;
20 void add_edge(int u,int v,LL dis){
21     e[++mct].v=v;e[mct].nxt=hd[u];e[mct].dis=dis;hd[u]=mct;return;
22 }
23 int n;
24 LL B1,B2;
25 LL dis[mxn];
26 int a[20];
27 bool inq[mxn];
28 queue<int>q;
29 void SPFA(){
30     memset(dis,0x3f,sizeof dis);
31     q.push(0);inq[0]=1;dis[0]=0;
32     while(!q.empty()){
33         int u=q.front();q.pop();inq[u]=0;
34         for(int i=hd[u];i;i=e[i].nxt){
35             int v=e[i].v;
36             if(dis[v]>dis[u]+e[i].dis){
37                 dis[v]=dis[u]+e[i].dis;
38                 if(!inq[v]){
39                     inq[v]=1;
40                     q.push(v);
41                 }
42             }
43         }
44     }
45     return;
46 }
47 LL query(LL x){
48     LL res=0;
49     for(int i=0;i<a[1];i++){
50         if(dis[i]<=x)res+=(x-dis[i])/(LL)a[1]+1;
51     }
52     return res;
53 }
54 int main()
55 {
56     scanf("%d%lld%lld\n",&n,&B1,&B2);
57     int i,j;
58     for(i=1;i<=n;i++){
59         a[i]=read();
60         if(!a[i]){i--;n--;}
61     }
62     sort(a+1,a+n+1);
63     
64     for(i=1;i<=n;i++)
65         for(j=0;j<a[1];j++){
66             add_edge(j,(j+a[i])%a[1],a[i]);
67         }
68     SPFA();
69 //    for(i=0;i<a[1];i++)printf("%d ",dis[i]);
70     LL ans=query(B2)-query(B1-1);
71     printf("%lld\n",ans);
72     return 0;
73 }
邻接表

二:改成了不具体建边的写法,只需要1000+ms

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<queue>
 6 #define LL long long
 7 using namespace std;
 8 const int mxn=500010;
 9 int read(){
10     int x=0,f=1;char ch=getchar();
11     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
13     return x*f;
14 }
15 int n;
16 LL B1,B2;
17 LL dis[mxn];
18 int a[20];
19 bool inq[mxn];
20 queue<int>q;
21 void SPFA(){
22     memset(dis,0x3f,sizeof dis);
23     q.push(0);inq[0]=1;dis[0]=0;
24     while(!q.empty()){
25         int u=q.front();q.pop();inq[u]=0;
26         for(int i=2;i<=n;i++){
27             int v=(u+a[i])%a[1];
28             if(dis[v]>dis[u]+a[i]){
29                 dis[v]=dis[u]+a[i];
30                 if(!inq[v]){
31                     inq[v]=1;
32                     q.push(v);
33                 } 
34             }
35         }
36     }
37     return;
38 }
39 LL query(LL x){
40     LL res=0;
41     for(int i=0;i<a[1];i++){
42         if(dis[i]<=x)res+=(x-dis[i])/(LL)a[1]+1;
43     }
44     return res;
45 }
46 int main()
47 {
48     scanf("%d%lld%lld\n",&n,&B1,&B2);
49     int i,j;
50     for(i=1;i<=n;i++){
51         a[i]=read();
52         if(!a[i]){i--;n--;}
53     }
54     sort(a+1,a+n+1);
55     SPFA();
56     LL ans=query(B2)-query(B1-1);
57     printf("%lld\n",ans);
58     return 0;
59 }

 

转载于:https://www.cnblogs.com/SilverNebula/p/6131409.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值