1. 程式人生 > >字首和與字尾和(HDU6186)

字首和與字尾和(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;
}