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;
    }
    
  • 相关阅读:
    免费的视频、音频转文本
    Errors are values
    Codebase Refactoring (with help from Go)
    Golang中的坑二
    Cleaner, more elegant, and wrong(msdn blog)
    Cleaner, more elegant, and wrong(翻译)
    Cleaner, more elegant, and harder to recognize(翻译)
    vue控制父子组件渲染顺序
    computed 和 watch 组合使用,监听数据全局数据状态
    webstorm破解方法
  • 原文地址:https://www.cnblogs.com/Mangata/p/14287491.html
Copyright © 2011-2022 走看看