【BZOJ 2118】 2118: 墨墨的等式 (最短路)
2118: 墨墨的等式
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
【分析】
看了好久题解,感觉好机智啊。。
题目可以理解成经典的背包问题。只是他问你的是[L,R]区间中有多少个容积是恰好可以装满的。
然后这个范围很大啊,传统的暴力当然是行不通的了。
考虑一个可以被拼出来的x,他里面可以包含ai也可以不包含ai。设x%ai=b(0<=b<ai)
那么其实x+ai都可以被拼出来,显然。
所以我们只要对于一个b,求出最小的可以拼出来的x,那么一直加ai也是可以的。
直接把不同的b算出来的答案加起来就好了。
要证明的话只要说明两个东西:
1、不重复性,对于不同的可以拼出的x1,x2,如果他们%ai不同,那就不会算重复。
如果他们%ai相同,也不会算重复【233我在搞笑?】
2、不遗漏性,对于可以拼出来的x1,有一个模对应的b,那么肯定会算到嘛= =【233
所以就这样搞笑的证明了??
【想出来的人思路真的很妙【我根本看不出是最短路啊ORZ。。。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<vector> 9 using namespace std; 10 #define LL long long 11 #define Maxm 7000100 12 #define Maxn 500010 13 #define INF 1000000000000LL 14 15 int a[20]; 16 17 struct node 18 { 19 int x,y,next; 20 LL c; 21 }t[Maxm];int len=0; 22 23 // vector<int > e[Maxn],c[Maxn]; 24 25 int first[Maxn]; 26 27 void ins(int x,int y,LL c) 28 { 29 t[++len].x=x;t[len].y=y;t[len].c=c; 30 t[len].next=first[x];first[x]=len; 31 } 32 33 LL dis[Maxn]; 34 bool inq[Maxn]; 35 queue<int > q; 36 void spfa() 37 { 38 int st=0; 39 while(!q.empty()) q.pop(); 40 memset(inq,0,sizeof(inq)); 41 // memset(dis,63,sizeof(dis)); 42 for(int i=0;i<a[1];i++) dis[i]=INF; 43 q.push(st);dis[st]=0;inq[st]=1; 44 while(!q.empty()) 45 { 46 int x=q.front(); 47 for(int i=first[x];i;i=t[i].next) 48 { 49 int y=t[i].y; 50 // for(int i=0;i<e[x].size();i++) 51 // { 52 // int y=e[x][i]; 53 if(dis[y]>dis[x]+t[i].c) 54 { 55 dis[y]=dis[x]+t[i].c; 56 // dis[y]=dis[x]+c[x][i]; 57 while(!inq[y]) 58 { 59 inq[y]=1; 60 q.push(y); 61 } 62 } 63 } 64 inq[x]=0; 65 q.pop(); 66 } 67 } 68 69 LL get_ans(LL x,LL y) 70 { 71 if(x<y) return 0; 72 return (x-y)/a[1]+1; 73 } 74 75 int main() 76 { 77 int n; 78 LL L,R; 79 scanf("%d%lld%lld",&n,&L,&R); 80 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 81 sort(a+1,a+1+n); 82 memset(first,0,sizeof(first)); 83 for(int i=1;i<=n;i++) 84 for(int j=0;j<a[1];j++) 85 { 86 ins(j,(j+a[i])%a[1],a[i]); 87 } 88 spfa(); 89 LL ans=0; 90 for(int i=0;i<a[1];i++) 91 ans+=get_ans(R,dis[i])-get_ans(L-1,dis[i]); 92 printf("%lld\n",ans); 93 return 0; 94 }
WA了很久竟然是spfa打错了。。。
2017-01-13 19:05:54