众所周知,我的博客园断更很长时间了。。。但是最近突然发现博客园可以用 markdown 然后就回来用一次。
题目链接:点我
30pts做法:
直接暴力一下就好了,(n^2) 枚举 (i,j),然后 (O(1)) 计算贡献即可。
50pts做法:
使用 (cnt[x]) 数组表示 (1-m) 中所有数中,二进制下第 (x) 位有多少个是 (1),那么 (sum_{i=0}^{m} p or i) 就可以直接
将 (p) 二进制拆分,然后如果第 (k) 位是 (0),那么这一位对答案的贡献就是这一位 (1) 的个数,而如果是 (1)的话
就和 (cnt[k]) 无关了,而贡献直接就是 (m+1),所以 (sum_{i=0}^{m} p or i) 这个式子就很好算了。然后我们枚举
每一个 (i,1leq ileq n),然后把每一个 (i) 当成 (p) ,然后就能 (O(nlog n)) 做了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int NR=1e5+10;
const int mod=1e9+7;
int n,m;
ll cnt[50];
ll ans;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int main()
{
n=read(),m=read();
for(int i=0;i<=m;i++)
{
int tot=0,t=i;
while(t)
{
tot++;
cnt[tot]+=(t&1);
t>>=1;
}
}
for(int i=0;i<=n;i++)
{
int t=i;
for(int x=1;x<=30;x++)
{
ans+=(t&1)?((1ll*m+1<<x-1)%mod):((1ll*cnt[x]<<x-1)%mod);
ans%=mod;
t>>=1;
}
}
printf("%lld",ans%mod);
return 0;
}
100pts做法:
考虑在50pts上优化,我直接对于 (n,m) 都初始化出来一个 (cnt),(cnt[x]) 表示第 (x) 位上有几个 (0),分别记为
(cnt1,cnt2),然后对于每一位处理,答案就是 (n imes m-cnt1[x]times cnt2[x]),但是由于 (n,m) 都是从 (0)
开始,所以是要把 (n++,m++)。记住,一定要及时取模,我考试的时候就因为取模问题 (100pts->50pts)。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int NR=1e5+10;
const int mod=1e9+7;
ll n,m;
ll cnt[NR],cnt2[NR];
ll ans;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int main()
{
n=read()+1,m=read()+1;
for(int i=1;i<=60;i++) cnt[i]=m-(((m>>i)<<i-1)+max(0ll,(m%(1ll<<i)-(1ll<<i-1))));
for(int i=1;i<=60;i++) cnt2[i]=n-(((n>>i)<<i-1)+max(0ll,(n%(1ll<<i)-(1ll<<i-1))));
for(int i=1;i<=60;i++) ans+=((n%mod*(m%mod)%mod-cnt[i]%mod*(cnt2[i]%mod)%mod)*((1ll<<i-1)%mod))%mod,ans%=mod;
printf("%lld",(ans%mod+mod)%mod);
return 0;
}