首页 > 其他分享 >二进制拆位

二进制拆位

时间:2024-05-18 15:53:21浏览次数:13  
标签:pre 二进制 tt int ans -- 拆位 mod

二进制拆位

题意:给定一个数组,求所有子区间的区间异或和的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。 小美拿到了一个数组,她想知道该数组的所有连续子数组的权值和是多少?答案对\(10^9+7\)取模。

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

给定一个数组,求 \(\sum_{i=1}^n\sum_{j=1}^n(a_i\oplus a_j)^2\)G. 沃托里村 - 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\) 的数组 \(a\) 和 \(q\) 次询问。我们定义 \(\operatorname{f}(l,r)(1\le l\le r\le n)\) 表示 \(a_l\And a_{l+1}\& \dots\& a_{r-1}\&a_r\) 的结果。其中,\(\&\) 表示位与运算。对于每次询问,将给定 \(l,k\)。请你找到最大的 \(r\) 使得 \(\operatorname{f}(l,r)\ge k\)。如果无解,输出 -1

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

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,x\(\sum_{i=l}^rxordist(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;
     }
     
}

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

给定一个数组,求\(\sum_{l=1}^n\sum_{r=l}^n(a_l\bigoplus a_{l+1}\bigoplus \dots\bigoplus a_r)\times\min_{l\leq i\leq r}a_i\)

先用单调栈套路地得到左右边界,对于左右边界,统计左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

标签:pre,二进制,tt,int,ans,--,拆位,mod
From: https://www.cnblogs.com/mathiter/p/18199393

相关文章

  • 2024-05-15:用go语言,考虑一个整数 k 和一个整数 x。 对于一个数字 num, 在其二进制表示
    2024-05-15:用go语言,考虑一个整数k和一个整数x。对于一个数字num,在其二进制表示中,从最低有效位开始,我们计算在x,2x,3x等位置处设定位的数量来确定其价值。举例说明,若对于x=1,num=13,则二进制表示为000001101,对应的价值为3。又如,当x=2,num=13,二进制表示依然为000001101,但对......
  • 格雷码和二进制的转换
    格雷码和二进制的转换方法如下:二进制码转换成格雷码:方法是从二进制码的最右边一位(最低位)起,依次将每一位与左边一位进行异或运算,作为对应格雷码该位的值,而最左边高位不变。对应公式为:g[n]=b[n],g[i]=b[i]xorb[i+1](i∈N,n-1≥i≥1),其中g、b分别对应n位的格......
  • 代码(CODE)到二进制(BIN)文件的编译过程
    这些步骤将源代码转换成可以在目标硬件上执行的机器代码。以下是这个过程的一般描述:预处理(Preprocessing):源代码(如.c、.cpp、.s等)首先被预处理。预处理器处理源文件中的宏定义、条件编译指令、包含指令(如#include)等。预处理器的输出通常是一个.i或.ii文件,它包含了所有宏替......
  • AlmaLinux 9.4 正式版发布 - RHEL 二进制兼容免费发行版
    AlmaLinux9.4正式版发布-RHEL二进制兼容免费发行版由社区提供的免费Linux操作系统,RHEL二进制兼容发行版请访问原文链接:AlmaLinux9.4正式版发布-RHEL二进制兼容免费发行版,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org由社区提供的免费Linux操作系......
  • 十进制转二进制(等)倒数取余法的原理
    十进制转二进制(等)倒数取余法的原理我们知道:十进制转二进制(八进制等)的方法是倒数取余法,但很多同学只是死记硬背,并不理解为什么这么做.让我们用下面的例子来理解一下:把十进制数字17转换为二进制首先把17分解为$2^n$之和:$17=1*2^3+0*2^2+0*2^1+1*2^0$.......
  • binaascii:A Python 在二进制和 ASCII 之间转换
    binaascii是一个用于在二进制和ASCII之间转换的模块。b2a_base64是binaascii模块中的一种方法,它将base64数据转换为二进制数据。下面是这个方法的一个例子:importbase64importbinasciimsg="Tandrew"encoded=msg.encode('ascii')base64_msg=base64.b64encode......
  • struct:Python二进制数据结构
    在C/C++语言中,struct被称为结构体。而在Python中,struct是一个专门的库,用于处理字节串与原生Python数据结构类型之间的转换。本篇,将详细介绍二进制数据结构struct的使用方式。函数与Struct类struct库包含了一组处理结构值得模块级函数,以及一个Struct类。格式指示符将由字符串格......
  • 无符号整数转二进制字符串逆序输出
    无符号整数转二进制逆序在C语言中,要实现一个函数来传入一个八位无符号整数,并返回其二进制倒序的字符串,你可以使用以下步骤:分配足够的堆空间来存储倒序后的二进制字符串。利用位运算符获取当成8位无符号整数的二进制数可以从高位往遍历也可以从低位往高位遍历从高遍......
  • mORMot 1.18 第12章 Blobs(大二进制对象)
    mORMot1.18第12章Blobs(大二进制对象)有些情况下,mORMot会以BLOBs(大二进制对象)的形式保存和检索数据。TSQLRawBlob属性用于存储像图片和文件这样的二进制数据。以TDynArray.SaveTo二进制格式存储的动态数组。明确注册为BLOBs的记录。当从数据库中存储/检索时,BLOBs以Base64......
  • 模拟集成电路设计系列博客——6.2.1 二进制权重电阻转换器
    6.2.1二进制权重电阻转换器一种主流的实现D/A转换器的方式是将一组信号以二进制方式进行组合。这组二进制信号可以是电流(在电阻或者电流方式中),但二进制权重的电荷也经常使用。在这个章节中,将首先介绍店主方式,然后是和电荷重分布的模式和电流模式。在这个远离下并不能保证单调性,......