Lottery Gym - 102822L
题意:
有n个盒子,每个盒子有x个球,每个球的数值为2a,问最多能组成多少数?答案mod 1e9+7
题解:
二进制思维题,浓浓的cf风格
参考题解
我们将数按照幂次进行排序(从小到大),然后对于每一位i,我们考虑下一位i+1的位置是否可以用第i位表示出来
比如:当前位是(2,5),下一位是(4,1)
当前位是有5个22,下一位是24,我们用4个22就可以表示出24,说明第i+1位可以被第i位表示。然后我们把第i位所能表示第i+1位的数量加给原本第i+1位的数量,看第i+1位是否能表示第i+2位,依次类推
如果可以被表示,我们就把第i位和第i+1位看作是一个连续的段,然后我们求出所有的段,把一个段内的所有指数a都化解成这个段内最小指数amin,段内相加,段与段之间乘积,得到答案
例子:
n=3 (1,3),(2,3),(4,1)
我们发现他们是一个连续的段内,这个段内最小的指数为1,所有都化成这个指数。(4,1)可以化成(2,4),加上原先的(2,3)有(2,7),(2,7)又可以化成(1,14),原先就有(1,3),再加上题木允许什么也不选,所以最后答案就是:(1 * 4 +3) * 2+3+1
这是一个段的答案,如果有多个段,答案相乘(这个不选,每一段都可以这样选)
为什么这样?我也是看了其他人的题解才知道这个方法,但是为什么这样做呢?
如果第i个位可以表示出第i+1个位,说明第i个位和第i+1个位之间的数均可以表示,所以我们可以看作一段。我们将所有指数都化成该段最低位指数amin,有x个amin,而其能表示的数就是t * amin , t属于0~x,所以答案是x+1
段与段之间是独立的,无法互相表示,所以不同段的结果乘积
比如有:(2,7)和(6,3),分别属于两个段
(2,7)所能表示的是:4,8,12…28
(6,3)所能表示的是: 64,128,192
两者可以彼此组合,且不会重复,如果会重复,按照性质就会属于一个段
另:
如果两个相邻的箱子的指数差距大于32的话,那么他们是一定不会组成相同段的,因为x最多1e9个能满足的a的差距就这么大了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;struct Node{long long a,x;
}a[100010];
long long b[100010];
bool cmp(Node a,Node b){return a.a<b.a;
}
long long q_pow(long a,long b){long long res = 1;a %= mod;while(b){if(b & 1) res = res * a % mod;a = a * a % mod;b >>=1 ;}return res;
}
int main(){int t;scanf("%d",&t);for(int ti=1;ti<=t;ti++){int n;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i].a,&a[i].x);sort(a+1,a+n+1,cmp);for(int i=1;i<=n;i++)b[i]=a[i].x;long long ans=1;for(int l=1,r;l<=n;l=r+1){r=l;// a[r+1].a-a[r].a表示指数差, (b[r]>>(a[r+1].a-a[r].a))为正说明第r位可以表示第r+1位 while(r < n && (b[r]>>(a[r+1].a-a[r].a)) && a[r+1].a - a[r].a <= 32){//从l开始求所有组成的最长连续段 b[r+1]+=b[r]>>(a[r+1].a-a[r].a); //将第r位所能表示的第r+1位的数量加到原先第r+1位的数量 r++;}for(int i=r;i>l;i--){//对段内的指数全部分解 a[i-1].x=(a[i-1].x+a[i].x*q_pow(2ll,a[i].a-a[i-1].a))%mod;}ans=ans*(a[l].x+1)%mod;}// printf("%lld\n",ans);printf("Case #%d: %lld\n",ti,ans);}
}