题目:L. Lottery
题意:n个盒子,每个盒子有x[i]个
2
a
[
i
]
2^{a[i]}
2a[i]的球编号,求每个盒子里取0-x[i]范围内的编号总和有多少个不同的。
带重复的总数很好求,直接累乘,但是去重情况套的很多,没办法下手,需要考虑需要一段是否重叠的二进制串。直接考虑每一个数的贡献,如果当前的数不影响到下一个数,直接乘这一段即可,影响的话找到受影响的一段。
比方说从i开始,类似与多重背包的二进制拆分优化,处理出一段之后,比方说是
2
i
到
2
j
2^i到2^j
2i到2j这段区间不会影响下一个区间了,那么他是由
2
i
2^i
2i递推过来的途中所有的数都会被影响,将x拆分为二进制可以组成0-x所有的数,整个一段同除以
2
i
2^i
2i,相当于所有的除以
2
i
2^i
2i的总数,最后隔段相乘得答案。
#include<bits/stdc++.h>
#define mmp make_pair
#define inf 0x3f3f3f3f
#define llinf 0x7fffffffffffffff
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
typedef double ld;
const ll mod=1e9+7;
const int maxn=1e5+10;
struct A {
ll num,x;
}a[maxn];
bool cmp(A t1,A t2) {
return (t1.num<t2.num);
}
ll b[maxn];
int book[maxn];
ll qpow(ll a,ll n) {
ll res=1;
while(n) {
if(n&1) res=(res*a)%mod;
a=(a*a)%mod;
n>>=1;
}
return res;
}
ll cal(ll n) {
ll sum=0;
while(n) {
n>>=1;
++sum;
}
return sum;
}
int main() {
int T;
scanf("%d",&T);
int cases=1;
while(T--) {
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i) {
scanf("%lld %lld",&a[i].num,&a[i].x);
}
int so=1;
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;++i) {
b[i]=a[i].x;
}
ll ans=1;
for(int i=1;i<n;++i) {
ll temp=a[i+1].num-a[i].num;
ll tn=cal(b[i]);
if((tn-1)<temp) {
book[i]=so;
so++;
continue;
}
book[i]=so;
b[i+1]+=b[i]/(1 << temp);
}
book[n]=so;
// for(int i=1;i<=n;++i)
// cout<<book[i]<<" ";
// continue;
for(int i=1;i<=n;++i) {
int j=i+1;
while(j<=n) {
if(book[j]==book[i]) ++j;
else break;
}
ll sum=a[i].x;
for(int k=i+1;k<j;++k) {
ll temp=a[k].num-a[i].num;
sum=(sum+(a[k].x*qpow(2,temp))%mod)%mod;
}
++sum;
sum%=mod;
ans=(ans*sum)%mod;
i=j-1;
}
printf("Case #%d: %lld\n",cases++,ans);
}
return 0;
}