题面
可以证明,在最后一次
÷2
操作之前,不必要进行连续的两次
−1
操作。因为连续的
−1
操作可以通过先
÷2
来减少操作次数。
我们定义
popcount(x)
来表示
x
二进制表示中
1.
−p
(p<2k)
,耗费
popcount(p)
次操作。
2.
÷2k
,耗费
k
次操作。
3.
可以发现,经过前两次操作,一些数x变成了
设这些余数为
那我们会得到
k∗n
个序列,每一个序列都有一个最少已经消耗的操作次数
k+minpopcount(Ci−1,Ci]
,但是两个差分之后相同的序列是可以通过操作3相互转化的。
那么我们把差分序列相同的分成一类,那么一类中首项相同也就序列相同,每个序列的首项有一个取值范围
[a1−K+k+minpopcount(Ci−1,Ci],a1]
,那么就相当于给定若干区间求并集长度的问题了,每一类DP一下即可。
总复杂度
O(60∗n2∗log(n∗60))
。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=1000000007;
int n,top,num;
ll K,a[210],c[210],d[210];
pair<ll,ll> qj[12010];
struct node
{
ll t[210],a1;
int k;
}s[12010];
bool operator <(const node &a,const node &b)
{
for(int i=1;i<n;i++)
if(a.t[i]<b.t[i]) return 1;
else if(a.t[i]>b.t[i]) return 0;
return 0;
}
bool check(node &a,node &b)
{
for(int i=1;i<n;i++)
if(a.t[i]!=b.t[i]) return 0;
return 1;
}
int maxpop(ll x,ll y)
{
if(x==-1) return 0;
int re=0;
for(int i=60;i>=0;i--)
if(((x>>i)&1)==((y>>i)&1)) re+=((x>>i)&1);
else {re++;break;}
return re;
}
ll dp()
{
sort(qj+1,qj+num+1);
ll re=0,l=0,r=-1;
for(int i=1;i<=num;i++)
{
if(qj[i].first>r) {re+=(r-l+1);l=qj[i].first;}
r=qj[i].second;
}
re+=(r-l+1);
return re;
}
int main()
{
scanf("%d%lld",&n,&K);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
sort(a+1,a+n+1);
for(int i=0;i<=60;i++)
{
for(int j=1;j<=n;j++)
c[j]=(a[j]&((1ll<<i)-1));
sort(c+1,c+n+1);
c[0]=-1;
int sz=unique(c+1,c+n+1)-c-1;
for(int j=1;j<=sz;j++)
if(a[1]>=c[j])
{
int P=maxpop(c[j-1],c[j]);
for(int l=1;l<=n;l++)
d[l]=((a[l]-c[j])>>i);
s[++top].a1=d[1];s[top].k=i+P;
for(int l=1;l<n;l++)
s[top].t[l]=d[l+1]-d[l];
}
}
sort(s+1,s+top+1);
num=0;
ll ans=0;
for(int i=1;i<=top;i++)
{
if(i!=1&&!check(s[i],s[i-1])) ans=(ans+dp())%mod,num=0;
if(s[i].k<=K)qj[++num]=make_pair(max(s[i].a1-K+s[i].k,0ll),s[i].a1);
}
ans=(ans+dp())%mod;
printf("%lld",ans);
return 0;
}