前缀和与后缀和(HDU6186)

题目链接。题目的大意是:给一个数组,和一个数组的下标·,然后在数组中去掉这个下标对应的元素,把剩下的元素全部做&/|/^这三种位运算,输出位运算之后的结果。数据范围1e5.当然暴力是不可行的。

首先需要知道的是:一个数&自己不变,|自己也是不变,^自己是0。这样我们对于每一种运算维护两个数组,一个前缀数组,一个后缀数组。这样两个结合起来可以达到去除任意一个中间元素的效果。

//我们只证明一种情况,其他的类似。证明&运算时候这种思想的正确性
//设S[i]=a[1]&a[2]....a[i],E[i]=a[i]&a[i-1}...&a[1].
//由于位运算是满足交换律的,所欲对于任意一个下标k我们假设结果是T,也就是
//T[k]=a[1]&a[2]...a[k-1]a[k+1]...a[n].就是把k去掉这个式子其实等于
//S[k-1]=a[1]$a[2]....a[k-1],E[k+1]=a[k+1]&a[k]&a[k-1]...&a[1]
//T[k]=S[k-1]&E[k+1]所以只需要预处理这样的前缀和与后缀和即可

这样我们就可以给出具体的实现的代码:

#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
const int maxn = 1e5+ 10;
int a[maxn];
using namespace std;
int sa[maxn], sb[maxn], sc[maxn];
int ea[maxn], eb[maxn], ec[maxn];
int main()
{
	ios::sync_with_stdio(false);
	int n, q;
	while (cin >> n >> q)
	{
		for (int i = 1; i <= n; i++)
		{
			cin >> a[i];
		}
		sa[1] = sb[1] = sc[1] = a[1];
		for (int i = 2;i <= n; i++)
		{
			sa[i] = sa[i-1] & a[i];
			sb[i] = sb[i-1] |a[i];
			sc[i] = sc[i-1] ^ a[i];
		}
		ea[n] = eb[n] = ec[n] = a[n];
		for(int i=n-1;i>0;i--)
		{
			ea[i] = ea[i+1] & a[i];
			eb[i] = eb[i+1] | a[i];
			ec[i] = ec[i+1] ^ a[i];
		}
		int tem;
		for (int i = 0; i <q; i++)
		{
			cin >> tem;
			if (tem == 1)
				cout << ea[tem + 1] << " " << eb[tem + 1] << " " << ec[tem + 1] << endl;
			else if (tem == n)
				cout << sa[tem - 1] << " " << sb[tem - 1] << " " << sc[tem - 1] << " " << endl;
			else
				cout << (sa[tem - 1] & ea[tem + 1]) << " " << (sb[tem - 1] | eb[tem + 1]) << " " << (sc[tem - 1] ^ ec[tem + 1]) <<endl;
		}
	}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41863129/article/details/83211995