题意:有n个数存放在数组a1[]中,然后数组a1[]生成数组a2[],a2[]生成a3[].....一直生成an[],公式是ai[k]=ai-1[k]&ai-1[k+1],现在如果把初始数据也就是数组a1[]里的某一个数换成另一个数,求所有数组的总和是多少。
分析:既然这里的数组生成公式里用到了位与运算,那么就用位运算来做了。
把数字写成二进制,公式的意思就是说同一数组的相邻数字的对应二进制位相与生成下一数组的一个数字的对应二进制位。先求出不做改变前的总和,然后每次改变通过替换数字与原数字每一位的差异来计算由该二进制位的差异引起的总和的差异,也就是说这里计算的基本单位不是每一个数字,而是数字的每一位。具体过程在草稿纸上画图,把a1[]数组的数字写成二进制形式,然后排成一列,对照每一位来找规律。另外预处理总和不能用模拟的方法O(n^2)似乎会超时,也是用位来计算。
小技巧:1<<i 可以表示第i位二进制位的权,也可以与别的数字相与获取该数字的第i位二进制位,从而判断该数字的这一位是1还是0
代码:
#include<iostream> #include<cstdio> #define N 100005 using namespace std; int n,m,a[100005],x,y; long long sum; void init() //预处理不做改变前的所有数组的和 { for(int i=0;(1<<i)<N;i++){ int k=1<<i; long long tot=0; for(int j=1;j<=n;j++){ if(k&a[j]) tot++; else{ sum+=tot*(tot+1)/2*(long long)k; tot=0; } } if(tot) sum+=tot*(tot+1)/2*(long long)k; } } void work(int x,int y) //当替换第x个数字为y时的总和 { for(int i=0;(1<<i)<N;i++){ int k=1<<i; if((k&y)==(k&a[x])) continue; long long l=0,r=0; for(int p=x-1;p>0;p--) if(k&a[p]) l++; else break; for(int p=x+1;p<=n;p++) if(k&a[p]) r++; else break; long long tmp=(l*r+l+r+1)*(long long)k; if(k&y) sum+=tmp; else sum-=tmp; } } int main() { cin>>n>>m; sum=0; for(int i=1;i<=n;i++) cin>>a[i]; init(); while(m--){ cin>>x>>y; work(x,y); cout<<sum<<endl; a[x]=y; } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-25 12:19:17