zoukankan      html  css  js  c++  java
  • Codeforces Round #626 D. Present 异或按位确定 +二分or双指针

    Codeforces Round #626 D. Present 异或按位确定 +二分or双指针

    题意

    给n个数,求他们两两的和的异或结果 n(4e5) 值域(1e7)

    思路

    异或问题一般都是按位确定,那么怎么确定第k位的值呢。首先第k位的值只和[1,k]位有关系,也就是说只跟a[i]本来在这一位有的数和进位有关系。那么怎么处理和的问题呢。首先我们只考虑[1..k]位,那么他们的和记为sum,如果sum在第k位有值 那么sum的值为 (2^k+x)因为只考虑[1..k]位所以a的最大值为(2^{k+1}-1) sum的最大值也就是(2^{k+2}-2)所以我们可以得到当([2^k,2^{k+1}-1])以及[2{k+1}+2{k}-1,2^{k+2}-2]如果sum落在这两个区间上,那么它的第k位就是1的。我们要算的是有几对和是位于这两个范围内的,很显然,我们可以先对a数组mod(2^{k+1})排序后利用二分找范围。这样复杂度就是0(nlognlogc)同样的 因为满足单调性,也可以运用双指针这样就少了一个log变成O(nlogc)

    二分

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define F first
    #define S second
    #define mkp make_pair
    #define pii pair<int,int>
    typedef long long ll;
    #define int ll
    const int inf=0x3f3f3f3f;
    const int maxn=4e5+5;
    const int mod= 998244353;
    int b[maxn],a[maxn];
    int n;
    int solve(int x,int y,int z){
     
    		if(y<x)return 0;
    		int l=lower_bound(b+1,b+1+n,x)-b;
    		int r=upper_bound(b+1,b+1+n,y)-b;
    			//cout<<x<<" fuck"<<y<<" "<<(n-l+1)-(n-r+1)<<endl;
    		int flag=0;
    		if(z>=x&&z<=y)flag=1;
    		return (n-l+1)-(n-r+1)-flag;
    }
    int32_t main(){
    	scanf("%lld",&n);
    	int up=0;
    	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),up=max(up,a[i]);
    	int ans=0;
    	
    	for(int k=1;k<=2e7;k<<=1){
    		for(int j=1;j<=n;j++)b[j]=a[j]%(k*2ll);
    		sort(b+1,b+1+n);
    		int sum=0;
    		for(int j=1;j<=n;j++){
    		//	cout<<solve((1<<(k-1))-b[j],(1<<k)-b[j]-1)<<" "<<solve((1<<(k-1))+(1<<(k))-b[j],(1<<(k+1))-2-b[j]);
    			//cout<<endl;
    		//	cout<<j<<" "<<((1<<(k-1))-b[j])<<" "<<((1<<k)-b[j]-1)<<" "<<solve((1<<(k-1))-b[j],(1<<k)-b[j]-1)<<endl;
    			//cout<<j<<" "<<((1<<(k-1))+(1<<(k))-b[j])<<" "<<((1<<(k+1))-2-b[j])<<" "<<solve((1<<(k-1))+(1<<(k))-b[j],(1<<(k+1))-2-b[j])<<endl;
    			sum+=solve(k-b[j],2ll*k-b[j]-1,b[j]);
    			sum+=solve(2ll*k+k-b[j],k*4ll-2-b[j],b[j]);
    		}
    		sum/=2;
    	//	cout<<sum<<endl;
    		if(sum&1)ans^=(k);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    双指针

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define F first
    #define S second
    #define mkp make_pair
    #define pii pair<int,int>
    typedef long long ll;
    const int inf=0x3f3f3f3f;
    const int maxn=4e5+5;
    const int mod= 998244353;
    int b[maxn],a[maxn];
    int n;
     
    int main(){
    	scanf("%d",&n);
    	int up=0;
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]),up=max(up,a[i]);
    	int ans=0;
    	for(int k=1;k<=2e7;k<<=1){
    		for(int j=1;j<=n;j++)b[j]=a[j]%(k*2ll);
    		sort(b+1,b+1+n);
    		long long  sum=0;
    		int p1=1,p2=1,p3=1;
    		for(int j=n;j>=1;j--){
    			while(p1<=n&&b[p1]+b[j]<k)p1++;
    			while(p2<=n&&b[p2]+b[j]<k+k)p2++;
    			while(p3<=n&&b[p3]+b[j]<k+k+k)p3++;
    			sum+=max(0,min(j,p2)-p1);
    			sum+=max(0,j-p3);
    		}
    		if(sum&1)ans^=(k);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    NKOJ P3051浇花
    Linux-Shell脚本编程-学习-2-Linux基本命令
    Linux-Shell脚本编程-学习-1-Linux基本命令
    Ubuntu下使用Git_6
    Ubuntu下使用Git_5
    电脑优化,提速
    Ubuntu下使用Git_4
    Ubuntu下使用Git_3
    Ubuntu下使用Git_2
    Ubuntu下使用Git_1
  • 原文地址:https://www.cnblogs.com/ttttttttrx/p/12444098.html
Copyright © 2011-2022 走看看