自测二的题都是数论,推出来题就很简单,推不出就发呆吧。淦!
众所周知,int范围内,搞位运算符一般开到32就够了。
给定一个长为n的数组,求解1到n内所有子集的位运算与之和mod(109+7),与即为c++中的&
Input
第一行为n,n<=1e5,接下来n个数为a[i]<=1e9
Output
输出结果取余(1e9+7)
input
3
1 2 3
output
9
input
3
2 3 3
output
17
首先,我们很容易想到去对二进制下每一位分别讨论。
枚举当前位k(假设是第k位),假设共有x个数当前位上为1,则有(n−x)个数当前位上为0。
0的情况:只要(n-x)中任选一个,与运算后都为0,所以贡献的值也为0,所以可以不用去考虑。
1的情况:因为它是 “与” &,当前位上为1的x个数,如果不是这x个都不选,都可以“与”出一个1,贡献 2^k 的值,这样一共有 2 ^ x-1种情况,所以第k位贡献的值为 ( 2 ^ k)*(2 ^ x- 1)。(x表示共有x个数当前位k上为1)
所以遍历每个数,得到每一二进制位有多少个,运算上述关系式相加即可。
#include
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
const int maxn=100+7;
ll n,m,x,c,y,mx;
ll sum;
ll a[maxn];
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<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
ll qpow(ll a, int b){
ll ans=1;
while(b){
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
int main(){
n=read();
for(int i=0;i<n;i++){
x=read(); c=0;
while(x){
if((x&1)) a[c]++;
x>>=1; c++;
}
mx=max(mx,c);
}
for(int i=0;i<mx;i++){
if(!a[i]) continue;
sum=(sum+((qpow(2,i)*(qpow(2,a[i])-1)))%mod)%mod;
// cout<
}
printf("%lld",sum);
}
给定一个长为n(n<=1e5)a[i]<=1e9的数组,给定两种操作:
id == 1:输入pos,val,将a[pos]的值修改为val
id == 2:输入l,r。求解区间l到r中所有子集的&和%(1e9+7)。
Input
第一行为n,m
第二行n个数代表a[i]
然后m次操作
修改操作: 1 x y,将Ax修改为y
询问操作: 2 l r,区间[l,r]中所有子集的位运算and之和 mod(109+7)
Output
对于每次询问输出一行,为该次询问的答案mod(109+7)。
input
3 6
1 2 3
2 1 3
1 1 2
2 1 3
2 2 3
1 2 5
2 1 3
output
9
15
7
13
一开始样例有点小错,但上面这个是对的。
无非就是加个线段树操作,只不过平时线段树上就储存一个数,这边是储存一个数组,因为这数组32大小,也可以理解为创建了32个线段树。
修改操作:将原来数二进制中有1的位置减一,将新的数中二进制中有1的位置加一
求和操作:算出l到r之间二进制情况,用一个辅助函数b[32]储存即可
计算:和上一题一样。
#include
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
#define ls i<<1
#define rs i<<1|1
const ll mod=1e9+7;
const int maxn=1e5+7;
ll n,m,x,y,a[maxn][32],k,b[32];
ll sum[maxn<<2][32];
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<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
ll qpow(ll a, int b){
ll ans=1;
while(b){
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
void pushup(int i){
for(int j=0;j<32;j++) sum[i][j]=sum[ls][j]+sum[rs][j]; }
void build(int l,int r,int i){
if(l==r){
for(int j=0;j<32;j++) sum[i][j]=a[l][j];
return;
}
int m=(l+r)>>1;
build(l,m,ls);
build(m+1,r,rs);
pushup(i);
}
void update(int l,int r,int i,int x,int k,int c){
if(l==r){
sum[i][k]+=c;
return;
}
int m=(l+r)>>1;
if(x<=m) update(l,m,ls,x,k,c);
else update(m+1,r,rs,x,k,c);
pushup(i);
}
void query(int l,int r,int i,int x,int y){
if(x<=l&&y>=r){
for(int j=0;j<32;j++) b[j]+=sum[i][j];
return;
}
int m=(l+r)>>1;
if(x<=m) query(l,m,ls,x,y);
if(y>m) query(m+1,r,rs,x,y);
}
void ccc(){
ll ans=0;
for(int i=0;i<32;i++){
if(!b[i]) continue;
ans=(ans+((qpow(2,i)*(qpow(2,b[i])-1)))%mod)%mod;
}
printf("%lld\n",ans);
}
int main(){
n=read(); m=read();
for(int i=1;i<=n;i++){
x=read(); y=0;
while(x){
a[i][y]=x&1;
x>>=1;
y++;
}
}
build(1,n,1);
while(m--){
k=read(); x=read(); y=read();
if(k==1){
k=0;
while(y){
if((y&1)&&!a[x][k]) update(1,n,1,x,k,1);
if(!(y&1)&&a[x][k]) update(1,n,1,x,k,-1);
a[x][k]=y&1;
y>>=1;
k++;
}
for(int i=k;i<32;i++){
if(a[x][i]){
update(1,n,1,x,i,-1);
a[x][i]=0;
}
}
}
else{
memset(b,0,sizeof(b));
query(1,n,1,x,y);
// for(int i=0;i<32;i++) cout<
// cout<
ccc();
}
}
}
给你一个集合,求所有子集异或和之和。
答案是将所有数或起来,然后乘上2^(n-1)就是答案了。
自己思考吧,不难