bzoj2118(加法原理)(墨墨的等式)
题目大意:给定n个物品,每个物品有一个非负价值,问[L,R]区间内有多少价值可以被凑出来。
题意网上一大片,具体求解过程是利用了加法原理,将各个模数拥有的个数之和相加。
就是说随机取一个数a[k],那么就是对于每个模数,通过转移的方式求出到达每个模数的最短路,将每个模数
0-(a[k]-1)之间的数连每个对应的a[j] (%a[k])意义下,花费为路的长度,这样就好了,最后前缀和相减求答案。
1 #include<cmath> 2 #include<queue> 3 #include<cstdio> 4 #include<vector> 5 #include<cstring> 6 #include<iostream> 7 #include<algorithm> 8 using namespace std; 9 10 typedef long long ll; 11 typedef pair<ll,int>fzy; 12 const ll INF=1e16+7; 13 14 15 int n; 16 int a[17]; 17 int cnt,head[500007],next[5000007],rea[5000007],val[5000007]; 18 ll ans=0,l,r,dis[500007]; 19 bool boo[500007]; 20 21 struct cmp 22 { 23 bool operator()(fzy x,fzy y) 24 { 25 return x.first>y.first; 26 } 27 }; 28 priority_queue<fzy,vector<fzy>,cmp>q; 29 30 void add(int u,int v,int fee) 31 { 32 cnt++; 33 next[cnt]=head[u]; 34 head[u]=cnt; 35 rea[cnt]=v; 36 val[cnt]=fee; 37 } 38 void Dijkstra() 39 { 40 for (int i=0;i<a[1];i++) 41 dis[i]=INF,boo[i]=0; 42 dis[0]=0; 43 q.push(make_pair(0,0)); 44 while (!q.empty()) 45 { 46 fzy now=q.top(); 47 q.pop(); 48 int u=now.second; 49 if (boo[u]) continue;boo[u]=1; 50 for (int i=head[u];i!=-1;i=next[i]) 51 { 52 int v=rea[i],fee=val[i]; 53 if (dis[v]>dis[u]+fee) 54 { 55 dis[v]=dis[u]+fee; 56 q.push(make_pair(dis[v],v)); 57 } 58 } 59 } 60 } 61 int main() 62 { 63 memset(head,-1,sizeof(head)); 64 scanf("%d%lld%lld",&n,&l,&r); 65 for (int i=1;i<=n;i++) 66 scanf("%d",&a[i]); 67 sort(a+1,a+n+1); 68 for (int i=0;i<a[1];i++) 69 for (int j=2;j<=n;j++) 70 add(i,(a[j]+i)%a[1],a[j]); 71 Dijkstra(); 72 for (int i=0;i<a[1];i++) 73 if (dis[i]<=r) 74 { 75 ll x=max((ll)0,(l-dis[i])/a[1]),y=(ll)(r-dis[i])/a[1]; 76 if (x*a[1]+dis[i]<l) x++; 77 if (y*a[1]+dis[i]>r) y--; 78 ans+=y-x+1; 79 } 80 printf("%lld\n",ans); 81 }