题意
题意大概是这样的有n(1<=n<=1e5)个不同的盒子每个盒子有两个值ai与xi,代表第i个盒子一共有xi(1<=xi<=1e9)个小球并且每个小球的价值为2^ai(0<=ai<=1e9)从这n个盒子里随意挑选小球(也可以不挑)问一共能选出多少种不同的价值。并且答案对1e9+7去模。
题解
其实拿到这题很容易就可以往二进制的方向上去想,那么二进制下怎么进位才能不影响答案呢?因为只要每位上都有数的话那么就不会导致组合的数量的损失,那么我们把这个二进制数在每位尽可能有数的情况下去尽可能的进位就行了,最后很容易可以得到一个只有0,1,2的串。又因为0222……22等于1111……10所以说只要被0隔开的数就可以看成独立的,那么每组独立的数的组合该怎么计算呢?因为每个独立组都不含0那么很容易就可以得到每个独立组的组合数的二进制就为当前串的二进制。又由于每个串之间都是相互独立的那么我们直接用乘法原理乘一下就得出答案了。
代码如下:
#include<iostream>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
const int mod=1e9+7;
const int N=3e6;
int cnt,node[N];
struct Node{
int x1,x2;
}now[N];
int ksm(int x,int y){
int ans=1;
while(y){
if(y&1) ans=(ll)ans*x%mod;
x=(ll)x*x%mod;
y>>=1;
}
return ans;
}
int main(){
int t,n;
int a,x,y,k,ansd;
scanf("%d",&t);
for(int i=1;i<=t;i++){
map<int,int> mp;
cnt=0;
scanf("%d",&n);
for(int j=1;j<=n;j++){
scanf("%d%d",&a,&x);
mp[a]=x;
node[j]=a;
}
sort(node+1,node+1+n);
int noww=0;
for(int j=1;j<=n;j++){
k=0;
if(noww>=node[j]+k){
continue;
}
while(mp[node[j]+k]>2){
if(mp[node[j]+k]%2){
mp[node[j]+k+1]+=mp[node[j]+k]/2;
mp[node[j]+k]=1;
now[++cnt].x1=node[j]+k;
now[cnt].x2=1;
}else{
mp[node[j]+k+1]+=mp[node[j]+k]/2-1;
mp[node[j]+k]=2;
now[++cnt].x1=node[j]+k;
now[cnt].x2=2;
}
noww=node[j]+k;
k++;
}
if(mp[node[j]+k]==1){
now[++cnt].x1=node[j]+k;
now[cnt].x2=1;
}else if(mp[node[j]+k]==2){
now[++cnt].x1=node[j]+k;
now[cnt].x2=2;
}
}
int ans=1,son=0,ansp=0;
for(ll j=1;j<=cnt;j++){
if(j==1){
son=0,ansp=0;
if(now[j].x2==2){
ansp=((ll)ansp+ksm(2,son))%mod;
}
}else{
if(now[j].x1!=now[j-1].x1+1){
ans=((ll)ans*(((ll)ksm(2,son+1)+ansp)%mod))%mod;
ansp=0;
son=0;
if(now[j].x2==2){
ansp=((ll)ansp+ksm(2,son))%mod;
}
}else{
son++;
if(now[j].x2==2){
ansp=((ll)ansp+ksm(2,son))%mod;
}
}
}
if(j==cnt){
ans=(ans*(((ll)ksm(2,son+1)+ansp)%mod))%mod;
}
}
printf("Case #%d: %d\n",i,ans);
}
return 0;
}