二进制拆位

二进制拆位

题意:给定一个数组,求所有子区间的区间异或和的sum

Sol:先做异或前缀和,原问题则变成求数组中任意两个数的异或,然后全部相加起来的结果。我们考虑每个元素每位的贡献,只需要统计前面(偏序计数)有多少个数的本位与自己不同。

//这个题目显然应该作为模板题,似乎没有找到直白的在原数组上作拆位前缀和 //本题先通过异或前缀和预处理,来将问题转化为两两之间的异或和, //如果不转化,就需要计算贡献次数 void solve(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)a[i]^=a[i-1]; vector<int>c0(len+2,1),c1(len+2,0); int ans=0; for(int i=1;i<=n;i++){ int u=a[i]; for(int j=len;j>=0;j--){ if((u>>j)&1){ ans+=(1<<j)*c0[j]; c1[j]++; } else { ans+=(1<<j)*c1[j]; c0[j]++; } } } cout<<ans<<endl; }

链接:https://ac.nowcoder.com/acm/contest/65051/D

小美定义一个数组的权值为:数组中任选两个数的异或之和。例如,数组[2,1,3]的权值为:(2 xor 1)+(2 xor 3)+(1 xor 3)=3+1+2=6。 小美拿到了一个数组,她想知道该数组的所有连续子数组的权值和是多少?答案对109+7取模。

Sol:注意题目意思。一个子数组的权值是两两异或和,那么求所有子数组的权值和。考虑继续按位算贡献,对于枚举到当前数a[i],我们计算他和每个前面的数计算的次数,当a[j](j<i)可以和其异或产生1<<k的贡献的时候,左端点有j个,右端点有n-i+1个,所以累加答案2k×j×(ni+1).那么我们需要预处理些什么呢?当前i,k是枚举的,我们显然不能枚举j,所以我们需要提前预处理有关j的前缀和,为什么能这样做的推导2k×j1×(ni+1)+2k×j2×(ni+1)=2k×(j1+j2)×(ni+1)。也就是说我们需要对于每一位记录0和1的下标前缀和

const int len = 30; const int mod = 1e9 + 7; vector<int>b0(40), b1(40); void solve() { cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; int ans = 0; for (int i = 1; i <= n; i++) { for (int j = len; j >= 0; j--) { int u = (a[i] >> j) & 1; if (u == 1) { b1[j]+=i;b1[j]%=mod; ans += b0[j] * (n - i + 1) % mod * (1LL << j) % mod; ans %= mod; } else { b0[j]+=i;b0[j]%=mod; ans += b1[j] * (n - i + 1) % mod * (1LL << j) % mod; ans %= mod; } } } cout << ans << endl; }

https://www.luogu.com.cn/problem/CF1879D Sum of XOR Functions

有一个序列 a,计算 $\sum\limits_{l=1}{n}\sum\limits_{r=l}n(r-l+1)\times \bigoplus\limits_{i=l}^{r}a_i $。

Sol:继续考虑贡献,枚举r,拆位前缀和维护符合要求下标L之和以及L的数量。不能全部开longlong,本题空间很紧张,不然会直接MLE.由于我们我们寻找的是所有在前缀和中能当左式的,本身就是L-1了,所以原本区间长度的减1就不用考虑了。

ll pre[N][30+2][2]; int num[N][30+2][2]; void solve(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)a[i]^=a[i-1]; for(int j=0;j<=30;j++){ num[0][j][0]=1; } for(int i=1;i<=n;i++){ for(int j=0;j<=30;j++){ int u=(a[i]>>j)&1; num[i][j][u]++; num[i][j][u^1]=num[i-1][j][u^1]; num[i][j][u]+=num[i-1][j][u]; pre[i][j][u]+=i; pre[i][j][u]+=pre[i-1][j][u]; pre[i][j][u]%=mod; pre[i][j][u^1]=pre[i-1][j][u^1]; } } ll ans=0; //枚举r,看有多少个L-1, for(int i=1;i<=n;i++){ //bug(i);cerr<<endl;// for(int j=0;j<=30;j++){ int u=(a[i]>>j)&1; ll cnt=num[i-1][j][u^1]; // bug(j);bug(cnt); // cerr<<endl; ll tmp=cnt*(i)-pre[i-1][j][u^1];tmp%=mod;tmp+=mod;tmp%=mod; ans=(ans+tmp*(1LL<<j)%mod)%mod; } } cout<<ans<<endl; }

给定一个数组,求 i=1nj=1n(aiaj)2G. 沃托里村 - 2023 年(第十五届)四川省大学生程序设计大赛重现赛 - ECNU Online Judge

Sol:本题需要考虑平方的特性,直接拆位以后,看2的次幂的贡献,考虑指数运算法则,只需要记录00,01,10,11的对数就可以了,本题不要求偏序,直接全部考虑不用去重。

dbeug:注意乘法与取模同等优先级,从左到右算,所以需要对于出现1<<60的式子需要先打括号取模,再计算。也可以预处理2的次幂mod意义下的。

int pre[N]; int dp[M][2][M][2]; void solve(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; pre[0]=1; for(int i=1;i<=63;i++)pre[i]=pre[i-1]*2%mod; for(int i=1;i<=n;i++){ for(int j=0;j<=30;j++){ for(int k=0;k<=30;k++){ int u1=(a[i]>>j)&1; int u2=(a[i]>>k)&1; dp[j][u1][k][u2]++; } } } int ans=0; for(int i=1;i<=n;i++){ for(int j=0;j<=30;j++){ for(int k=0;k<=30;k++){ int u1=(a[i]>>j)&1; int u2=(a[i]>>k)&1; ans=(ans+dp[j][u1^1][k][u2^1]%mod*(1LL<<(j+k))%mod)%mod; } } } cout<<ans<<endl; }

image-20240509195429268

image-20240509195440675

#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N = 2e5 + 10, mod = 998244353; LL a[N], pre[N], suf[N], s[N]; LL bit[2][35]; int main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; s[i] = s[i - 1] ^ a[i]; } for (int i = 0; i <= 30; i++) { bit[0][i]++; } for (int i = 1; i <= n; i++) { pre[i] = pre[i - 1]; for (int j = 0; j <= 30; j++) { int u = s[i] >> j & 1; pre[i] = (pre[i] + (1 << j) * bit[u ^ 1][j] % mod) % mod; } for (int j = 0; j <= 30; j++) { int u = s[i] >> j & 1; bit[u][j]++; } } memset(bit, 0, sizeof bit); for (int i = 0; i <= 30; i++) { bit[0][i]++; } for (int i = n; i >= 1; i--) { s[i] = s[i + 1] ^ a[i]; } for (int i = n; i >= 1; i--) { suf[i] = suf[i + 1]; for (int j = 0; j <= 30; j++) { int u = s[i] >> j & 1; suf[i] = (suf[i] + (1 << j) * bit[u ^ 1][j] % mod) % mod; } for (int j = 0; j <= 30; j++) { int u = s[i] >> j & 1; bit[u][j]++; } } memset(bit, 0, sizeof bit); LL ans = 0; for (int i = n; i >= 1; i--) { for (int j = 0; j <= 30; j++) { int u = s[i] >> j & 1; ans = (ans + pre[i - 1] * (1 << j) % mod * bit[u ^ 1][j] % mod) % mod; } for (int j = 0; j <= 30; j++) { int u = s[i] >> j & 1; bit[u][j] = (bit[u][j] + suf[i]) % mod; } } cout << ans << '\n'; return 0; }

https://codeforces.com/problemset/problem/1878/E按位与的拆位前缀和

t 组数据。每组数据给定长度为 n 的数组 aq 次询问。我们定义 f(l,r)(1lrn) 表示 al&al+1&&ar1&ar 的结果。其中,& 表示位与运算。对于每次询问,将给定 l,k。请你找到最大的 r 使得 f(l,r)k。如果无解,输出 -1

Sol:考虑与运算具有递减性,所以可以二分r。考虑怎么check。由于与运算不可逆,无法像异或那样快速求出按位与的区间和,我们只能预处理拆位的前缀和,然后每次花费O(logn)的时间去计算区间与和。具体就是看每一位1的个数是不搜等于区间长度,因为只要有1个0就是0了。时间复杂度:O(mlogn2)

int a[N]; //考虑预处理后花费O(log)求出区间与的和 void solve(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; vector<vector<int>>pre(n+1,vector<int>(32,0)); for(int i=1;i<=n;i++){ for(int j=30;j>=0;j--){ int u=(a[i]>>j)&1; if(u==1)pre[i][j]=1+pre[i-1][j]; else pre[i][j]=pre[i-1][j]; } } cin>>m; for(int i=1;i<=m;i++){ int ql,k;cin>>ql>>k; int l=ql;int r=n; auto check=[&](int x){ int res=0; for(int j=30;j>=0;j--){ if(pre[x][j]-pre[ql-1][j]==(x-ql+1))res+=(1<<j); } if(res>=k)return true; return false; }; if(a[ql]<k){cout<<-1<<" ";continue;} while(l<r){ int mid=(l+r+1)>>1; if(check(mid))l=mid; else r=mid-1; } cout<<l<<" "; } cout<<endl; }

https://vjudge.net/contest/602096#problem/E

给定一棵树,带边权,m次询问,每次给出l,r,xi=lrxordist(i,x),xordist为i到x上的路径异或起来。

Sol:考虑单点对单点的时候,钦定1为根不影响答案。算一下树上前缀和d[x].则答案就是d[i]^d[x].现在就是考虑多个数与一个数的异或和,我们需要在logn的时间内求出。预处理按位的0和1的数量,直接按每一位的贡献和数量计算。

int a[N]; #define a2 array<int,2> struct edge{int v,w;}; vector<edge>e[N]; int d[N]; void dfs(int u,int fa){ for(auto [v,w]:e[u]){ if(v==fa)continue; d[v]=d[u]^w; dfs(v,u); } } void solve(){ cin>>n; for(int i=1;i<=n;i++){d[i]=0;e[i].clear();} for(int i=1;i<=n-1;i++){ int u,v,w;cin>>u>>v>>w; e[u].push_back({v,w}); e[v].push_back({u,w}); } // for(int i=1;i<=n;i++)bug(d[i]); dfs(1,0); // for(int i=1;i<=n;i++)bug(d[i]); cin>>m; //for(int i=1;i<=n;i++)s[i]=d[i]^s[i-1]; vector<vector<a2>> pre(n+1, vector<a2>(31, a2())); for(int i=1;i<=n;i++){ for(int j=0;j<=30;j++){ int u=(d[i]>>j)&1; pre[i][j][u]=pre[i-1][j][u]+1; pre[i][j][u^1]=pre[i-1][j][u^1]; } } for(int i=1;i<=m;i++){ int l,r,x;cin>>l>>r>>x; int ans=0; for(int j=0;j<=30;j++){ int u=(d[x]>>j)&1; int cnt=pre[r][j][u^1]-pre[l-1][j][u^1]; ans=(ans+cnt*(1LL<<j)); } cout<<ans<<endl; } }

非常遗憾调试失败,还要赶进度,以后再回来吧

给定一个数组,求l=1nr=ln(alal+1ar)×minlirai

先用单调栈套路地得到左右边界,对于左右边界,统计左0右1地个数,左1右0个数,然后考虑拆位前缀和算贡献,思路不难,注意注意边界处理。

全局开了longlong,依然不知道wa哪里了

// Problem: I - 序列 // Contest: Virtual Judge - 2023杭电新生赛 // URL: https://vjudge.net/contest/602096#problem/I // Memory Limit: 256 MB // Time Limit: 3000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; #define ll long long # define int long long #define ull unsigned long long #define pii pair<int,int> #define db double #define baoliu(x, y) cout << fixed << setprecision(y) << x #define endl "\n" #define alls(x) (x).begin(), (x).end() #define fs first #define sec second #define bug(x) cerr << #x << " = " << x << endl #define bug2(x) cerr << #x << " = " << x <<" " const int N = 2e5 + 10; const int M = 1e6 + 10; const int inf = 0x3f3f3f3f; const int mod = 998244353; const double eps = 1e-8; const double PI = acos(-1.0); int n, m; #define a2 array<int,2> void solve(){ cin>>n; vector<int>a(n+1); for(int i=1;i<=n;i++)cin>>a[i]; stack<int>pre; stack<int>nxt; vector<int>l(n+1,0),r(n+1,0),s(n+1,0); for(int i=1;i<=n;i++)s[i]=s[i-1]^a[i]; for(int i=1;i<=n;i++){ while(!pre.empty()&&a[i]<=a[pre.top()])pre.pop(); if(!pre.empty())l[i]=pre.top()+1; else l[i]=1; pre.push(i); } for(int i=n;i>=1;i--){ while(!nxt.empty()&&a[i]<=a[nxt.top()])nxt.pop(); if(!nxt.empty())r[i]=nxt.top()-1; else r[i]=n; nxt.push(i); } int ans=0; // for(int i=1;i<=n;i++)cerr<<a[i]<<" "; // cerr<<endl; // for(int i=1;i<=n;i++)cerr<<l[i]<<" "; // cerr<<endl; // for(int i=1;i<=n;i++)cerr<<r[i]<<" "; // cerr<<endl; vector<vector<a2>>f(n+1,vector<a2>(32,a2())); // for(int i=0;i<=30;i++)f[0][i][0]=1; for(int i=1;i<=n;i++){ for(int j=0;j<=30;j++){ int u=(s[i]>>j)&1; f[i][j][u]=1+f[i-1][j][u]; f[i][j][u^1]=f[i-1][j][u^1]; } } // for(int i=1;i<=n;i++)cerr<<s[i]<<" "; // cerr<<endl; // bug(f[3][0][1]); // bug(f[3][1][1]); // bug(f[3][2][1]); // bug(f[0][0][1]); // bug(f[0][1][1]); // bug(f[0][2][1]); for(int i=1;i<=n;i++){ int res=0;int ql=l[i],qr=r[i]; // bug(i);bug(l[i]);bug(r[i]); for(int j=0;j<=30;j++){ int cl1,cl0;int cr1,cr0; cr0=f[qr][j][0]-f[i-1][j][0]; cl0=f[i-1][j][0]-f[max(ql-2,0LL)][j][0]; cl1=f[i-1][j][1]-f[max(ql-2,0LL)][j][1]; cr1=f[qr][j][1]-f[i-1][j][1]; if(ql-1<=0)cl0++; int cnt=cl1*cr0%mod+cl0*cr1%mod;cnt%=mod; //bug2(cl1);bug2(cl0);bug2(cr1);bug2(cr0); //bug(i);bug(j);bug(cnt);bug("___"); res=(res+cnt*((1LL<<j)%mod)%mod)%mod; } //bug(i);bug(res); ans=(ans+(res*a[i])%mod)%mod; } cout<<ans<<endl; } signed main() { cin.tie(0); ios::sync_with_stdio(false); //freopen("in.txt", "r", stdin); //freopen("out.txt", "w", stdout); //freopen("debug.txt", "w", stderr); int t=1; cin>>t; while(t--)solve(); return 0; }

给一份std参考

#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+10; const int mod=998244353; int a[N],pre[N]; int prexors[40][N]; int stk[N]; int l[N],r[N]; int getres(int l,int r,int x) { int res=0; for(int i=0;i<32;i++) { //vector<int> Lrange(2),Rrange(2); int Lrange[2],Rrange[2]; Lrange[1]=prexors[i][x-1]-(l?prexors[i][l-1]:0); Lrange[0]=x-l-Lrange[1]; Rrange[1]=prexors[i][r]-prexors[i][x-1]; Rrange[0]=r-x+1-Rrange[1]; int t=(Lrange[0]*Rrange[1]%mod+Lrange[1]*Rrange[0]%mod)%mod; t=(t*(1ll<<i))%mod; res=(res+t)%mod; } return res; } void kei() { int n; cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; int tt=0; for(int i=1;i<=n;i++) { while(tt&&a[stk[tt]]>a[i])tt--; l[i]=tt?stk[tt]:0; stk[++tt]=i; } tt=0; for(int i=n;i>=1;i--) { while(tt&&a[stk[tt]]>=a[i])tt--; r[i]=tt?stk[tt]-1:n; stk[++tt]=i; } for(int i=1;i<=n;i++)pre[i]=pre[i-1]^a[i]; for(int i=1;i<=n;i++)for(int j=0;j<32;j++)prexors[j][i]=(pre[i]>>j)&1; for(int i=0;i<32;i++)for(int j=1;j<=n;j++)prexors[i][j]+=prexors[i][j-1]; int res=0; for(int i=1;i<=n;i++)res=(res+getres(l[i],r[i],i)*a[i]%mod)%mod; cout<<res<<'\n'; } signed main() { ios::sync_with_stdio(false); cin.tie(nullptr),cout.tie(nullptr); int t=1; cin>>t; while(t--)kei(); }

https://ac.nowcoder.com/acm/discuss/blogs?tagId=268078

https://ac.nowcoder.com/acm/contest/80793/D


__EOF__

本文作者爱飞鱼
本文链接https://www.cnblogs.com/mathiter/p/18199393.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   potential-star  阅读(76)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示