【Luogu P5283】[十二省聯考2019]異或糉子

連接:

題目html

題目大意:

求一段數列前 \(k\) 大區間異或和的和。node

正文:

本題和 【Luogu P2048】[NOI2010] 超級鋼琴 思路極其類似。對於前 \(k\) 大,咱們能夠用堆來維護。spa

不一樣於超級鋼琴的是,本題是異或和,不能直接根據前綴和大小排序,那怎麼辦呢?考慮用 0/1Trie 解決。從高位到低位插入數字,順便記錄子樹大小,至於查詢,畢竟是第 \(k\) 大,咱們能夠像平衡樹同樣經過子樹大小肯定當前一位。code

代碼:

const int N = 2e7 + 10;

int n, k;
ll sum[N];

struct Trie
{
	int ch[N][2];
	ll siz[N], tot;
	void ins(ll val) 
	{
		int u = 0;
		for (int i = 31; ~i; --i)
		{
			bool k = (val >> i) & 1; siz[u]++;
			if (!ch[u][k]) ch[u][k] = ++tot;
			u = ch[u][k];
		}
		siz[u] ++;
		return;
	}
	
	ll query(ll val, int n) 
	{
		int u = 0;ll ans = 0;
		for (int i = 31; ~i; --i)
		{
			bool k = (val >> i) & 1;
			if(!ch[u][k ^ 1]) u = ch[u][k];
			else if (n <= siz[ch[u][k ^ 1]]) u = ch[u][k ^ 1], ans |= 1ll << i;
			else n -= siz[ch[u][k ^ 1]], u = ch[u][k];
		}
		return ans;
	}
}t;
struct node
{
	ll x, rk, val;
	inline bool operator < (const node& a) const
	{
		return val < a.val;
	}
};
priority_queue<node> q;
ll ans = 1;

int main()
{
//	freopen(".in", "r", stdin);
//	freopen(".out", "w", stdout);
	scanf ("%d%d", &n, &k);
	t.ins(0);
	for (int i = 1; i <= n; i++)
		scanf ("%lld", &sum[i]), sum[i] ^= sum[i - 1], t.ins(sum[i]);
	for (int i = 0; i <= n; i++)
		q.push((node){i, 1, t.query(sum[i], 1)});
	for (int i = 1; i <= 2 * k; i++)
	{
		node x = q.top(); ans += x.val; q.pop();
		if (x.rk < n) q.push((node){x.x, x.rk + 1, t.query(sum[x.x], x.rk + 1)});
	}
	printf ("%lld\n", ans / 2);
	return 0;
}
相關文章
相關標籤/搜索