zoukankan      html  css  js  c++  java
  • Home_W的位运算(位运算+预处理)

    Home_W的位运算1

    题目链接: 传送门
     
    解题思路:这题有两种解题思路,一种就是(n^2 imes m)的时间复杂度,还有一种就是经过预处理的时间复杂度为(n^2)的方法,先说第一种,大家直接按照题目要求的来,一行向量一行向量的进行比较久能AC,没有卡时间,先贴代码:
    code:

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N = 200;
    int mp[N][N];//mp记录的是n个向量
    int m,n;
    
    bool check(int a,int b) {
    	for(int i = 1;i <= m; ++i) {
    		if(mp[a][i] && mp[b][i])//按照题目要求的进行与,当然你也可以用一个&,
    			return true;    //因为此时的mp记录的是0,1离散值,所以两种判断都行
    	}
    	return false;
    }
    
    int main()
    {
    	int t;
    	while(~scanf("%d",&t)) {
    		while(t--) {
    			scanf("%d%d",&n,&m);
    			for(int i = 1; i <= n; ++i) {
    				for(int j = 1;j <= m; ++j) {
    					scanf("%d",&mp[i][j]);
    				}
    			}
    			int res = 0;
    			for(int i = 1;i < n; ++i) {
    				for(int j = i + 1; j <= n; ++j) {
    					if(check(i,j)) {
    						res ++;
    					}
    				}
    			}
    			printf("%d
    ",res);
    		}
    	}
    	return 0;
    }
    

    第二种思路就有意思了,我们可以通过向量的这些离散值,把每个向量看成二进制,然后把他还原成一个整数然后再用&进行判断,举个栗子,101B和010B,这两个二进制数,我们要判断他们两个是否有关系,我们可以直接从第一位开始与,一直到最后,我们发现这两个数没有关系,这不正是与运算(&)的运算法则吗,所以我们不需要手动的去从第一位一直比较到最后一位,直接把二进制转为十进制保存,然后(n^2)的判断,此时的你一定发现了,向量的维度最大时100的,long long是装不下的,嗯,此时需要一个大数,在这里可以使用C++内置的__int128这个数据类型,他能存储128位(二进制)的整数,但是输入输出需要自己手写,不过此题不用
    code:

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N = 105;
    __int128 key[N];
    int mp[N];
    int m,n;
    
    int main()
    {
    	int t;
    	while(~scanf("%d",&t)) {
    		while(t--) {
    			scanf("%d%d",&n,&m);
    			for(int i = 1; i <= n; ++i) {
    				for(int j = 1;j <= m; ++j) {
    					scanf("%d",&mp[j]);
    				}
    				__int128 ans = 0;
    				for(int j = m; j > 0; --j) {//这里进行预处理,把二进制转化为十进制
    					ans += mp[j] * (1 << (m - j));
    				}
    				key[i] = ans;
    			}
    			int res = 0;
    			for(int i = 1;i <= n; ++i) {
    				for(int j = i + 1; j <= n; ++j) {
    					if(key[i] & key[j]) {//O(1)的时间复杂度进行比较
    						res ++;
    					}
    				}
    			}
    			printf("%d
    ",res);
    		}
    	}
    	return 0;
    }
    

    Home_W的位运算2

    题目链接: 传送门
    解题思路:此题和上题,题目类似,只是数据有所变化,如果直接使用上题暴力解法,会T,再加上我们发现向量的维度只有30,也就是告诉我们,将一个向量转化为2进制用int就能存下,思路参见上题
    code:

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    
    const int N = 105,M = 35;
    bool mp[N][M];
    int key[N];
    int n,m;
    
    int main()
    {
    	int t;
    	while(~scanf("%d",&t)) {
    		while(t--) {
    			int ans = 0;
    			scanf("%d%d",&n,&m);
    			for(int i = 1;i <= n; ++i) {
    				for(int j = 1;j <= m; ++j) {
    					scanf("%d",&mp[i][j]);
    				}
    				int ans = 0;
    				for(int j = m; j > 0; --j) {//将向量的二进制转化为十进制
    					ans =ans + mp[i][j] * (1 << (m-j));
    				}
    				key[i] = ans;
    			}
    			for(int i = 1;i <= n; ++i) {
    				for(int j = i + 1; j <= n; ++j) {
    					for(int k = j + 1; k <= n; ++k) {
    						if((key[i] & key[j]) && (key[i] & key[k]) &&(key[j] & key[k])) {
    							ans++;
    						}
    					}
    				}
    			}
    			printf("%d
    ",ans);
    		}
    	}
    	return 0;
    }
    

    Home_W的位运算3

    题目链接: 传送门
    解题思路:很明显我们会想到先把b的数全都 xor 一遍,然后通过一位一位的比较,对每种情况分类讨论就行,具体请看代码,第二种方法就简单了,有一个数学结论 a^b=c => a = b ^ c
    我先贴结论写法的code:

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    
    ll a,b,c;
    
    int main()
    {
    	int t;
    	while(~scanf("%d",&t)) {
    	while(t--) {
    		scanf("%lld%lld",&b,&c);
    		ll ans = 0;
    		for(int i = 0;i < b; ++i) {
    			scanf("%lld",&a);
    			ans ^= a;
    		}
    		printf("%lld
    ",c^ans);
    	}
    }
    	return 0;
    }
    

    第二种手动模拟的code:

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    
    ll a,b,c;
    
    vector<int> p1,p2,p3;//p1对应的是异或后的b,p2对应的是c,p3对应的是x
    
    int main()
    {
    	int t;
    	while(~scanf("%d",&t)) {//多组输入
    	while(t--) {
    		scanf("%lld%lld",&b,&c);
    		ll ans = 0;
    		for(int i = 0;i < b; ++i) {
    			scanf("%lld",&a);
    			ans ^= a;
    		}
    
    		while(ans) {//把b转为二进制
    			int k = ans % 2;
    			p1.push_back(k);
    			ans /= 2;
    		}
    //		for(int i = p1.size() - 1; i >= 0; --i) {
    //			printf("%d%c",p1[i],i==0?'
    ':' ');
    //		}
    //		puts("---------------------");
    		while(c) {//把c转为二进制
    			int k = c % 2;
    			p2.push_back(k);
    			c /= 2;
    		}
    //		for(int i = p2.size() - 1; i >= 0; --i) {
    //			printf("%d%c",p2[i],i==0?'
    ':' ');
    //		}
    //		puts("---------------------");
    			for(int i = 0, len = max(p2.size(),p1.size());i < len; ++i) {//类似大数模拟的操作只不过大体分为三类,然后再细分情况
    				if(i + 1 > p1.size()) {
    					if(p2[i] == 1)
    						p3.push_back(1);
    					else
    						p3.push_back(0);
    				}
    				else if(i + 1 > p2.size()) {
    					if(p1[i] == 1)
    						p3.push_back(1);
    					else
    						p3.push_back(0);
    				}
    				else {
    					if(p2[i] == 1) {
    						if(p1[i] == 0)
    							p3.push_back(1);
    						else
    							p3.push_back(0);
    					}
    					else if(p2[i] == 0) {
    						if(p1[i] == 0)
    							p3.push_back(0);
    						else
    							p3.push_back(1);
    					}
    				}
    			}
    //		for(int i = p3.size() - 1; i >= 0; --i) {
    //			printf("%d%c",p3[i],i==0?'
    ':' ');
    //		}
    //		puts("---------------------");
    		ll res = 0;
    		for(int i = 0, len = p3.size();i < len; ++i) {
    			res = res + p3[i]*(1<<i);
    		}
    		printf("%lld
    ",res);
    		p1.clear();
    		p2.clear();
    		p3.clear();//注意清空操作
     }
    }
    	return 0;
    }
    

     
    小节:通过这三题我还是有很大的收获,比如长见识了a^b=c 可以把b除过去……,还有就是&运算符也有了更深的理解。
    updata:

    Home_W的位运算4

     
    原题链接:传送门
     
    解题思路:通过第三题我们可以知道异或有个性质即:(a^b=c => a=c^b),所以我们通过对序列进行前缀异或处理,然后我们将每次异或的结果,在vis数组里面++,表示的是连续的异或的值,然后我们从左到右对s^pre[i]的值进行加和,最后除2就行,注意这里我们需要将vis[0]初始化为1,因为可能有前缀异或结果就等于s的情况。
     
    Code:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 1000005;
    int a[N],b[N],n,q,s;
    int vis[N*10];
    
    int main()
    {
    	scanf("%d",&n);
    	vis[0] = 1;
    	for(int i = 1;i <= n; ++i) {
    		scanf("%d",&a[i]);
    		a[i] ^= a[i-1];
    		vis[a[i]]++;
    	}
    	
    	scanf("%d",&q);
    	while(q--) {
    		scanf("%d",&s);
    		int loc;
    		int ans = 0;
    		for(int i = 0;i <= n; ++i) {
    			loc = s ^ a[i];
    			ans += vis[loc];
    		}
    		printf("%d
    ",ans/2);
    	}
    	return 0;
    }
    
  • 相关阅读:
    for循环之初学者N多算法小练习
    Java数据类型(基本数据类型)学习
    Windows10 图标重建
    springMVC框架搭建
    Spring框架 jar包下载
    Hibernate配置文件中配置各种数据库链接
    Ajax第一课
    Windows 10 碎片整理程序使用
    python之restful api(flask)获取数据
    谷歌浏览器安装扩展程序
  • 原文地址:https://www.cnblogs.com/Mangata/p/14287491.html
Copyright © 2011-2022 走看看