Codeforces 1401F Reverse and Swap


Description

給一個長度為 $2^n$ 的數組 $a$,現在需要處理 $q$ 個詢問,每個詢問是以下 4 種類型之一:

  • $Replace(x, k)$ 把 $a_x$ 修改為 $k$;
  • $Reverse(k)$ 對於每一個 $i(i\ge 1)$,把區間 $[(i-1)\cdot 2^k+1, i\cdot 2^k]$ 的元素翻轉;
  • $Swap(k)$ 對於每一個 $i(i\ge 1)$,交換區間 $[(2i-2)\cdot2^k+1,(2i-1)\cdot2^k]$ 和 $[(2i-1)\cdot2^k+1,2i\cdot2^k]$ 的所有元素;
  • $Sum(l,r)$ 輸出區間 $[l,r]$ 中所有元素的和。

$n \le 18$,$q \le 10^5$

Background

個人認為挺巧妙的一道題,結果在神仙眼里就是:

(語言經過刪節)

Solution

操作寫的不是人話,翻譯一下四個操作:

1. $\operatorname{\large{R}\small{PLACE}}\normalsize{(x, k)}$,單點修改 $a_x \gets k$;
2. $\operatorname{\large{R}\small{EVERSE}}\normalsize{(k)}$,從左到右划分為若干個長度為 $2^k$ 的區間,將每個區間翻轉;
3. $\operatorname{\large{S}\small{WAP}}\normalsize{(k)}$,從左到右划分為若干個長度為 $2^k$ 的區間,相鄰兩個區間交換位置;
4. $\operatorname{\large{S}\small{UM}}\normalsize{(l, r)}$,求 $\sum\limits_{i=l}^r a_i$。

我會做 1 和 4 !

考慮巧妙利用線段樹,因為保證序列的長度為 $2^n$,所以,線段樹的形狀為:共 $n+1$ 層,令葉子結點為第 $0$ 層,根節點為第 $n$ 層,則第 $k$ 層的每個結點,維護的都是長度為 $2^k$ 的區間。

觀察操作 2 和 3,我們發現,線段樹第 $k$ 層的所有結點,恰好就是我們所想要修改的區間!

定義一個標記 $\text{rev}_{dep}$,如果為 $1$ 表示第 $dep$ 層的左右結點的左右兒子分別互換,為 $0$ 則不變。

先來看操作 3,如果我們將 $\text{rev}_{k+1}$ 改變,是不是,恰好相鄰兩個長度為 $2^k$ 的區間都互換了呢?

下圖展示 $n = 3$,$\operatorname{\large{S}\small{WAP}}\normalsize{(1)}$:

再看操作 4,不難發現,它等價於改變所有的 $\text{rev}_0 \sim \text{rev}_k$。這很好理解,$\text{rev}_{k}$ 改變后,相當於是每個長度為 $2^k$ 的區間內部,前 $2^{k-1}$ 和后 $2^{k-1}$ 個調換了位置,但這兩部分內部分別有序,所以要繼續調換下去。

在加入了 $\text{rev}$ 數組后,線段樹也不難實現,如果當前處在 $\text{rev}_{dep}=1$ 的一層,就把右兒子當作左兒子,左兒子當作右兒子好了。

時間復雜度 $\mathcal O(qn)$。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 20, S = 1 << N;
int n, q;
bool rev[N];
struct segment_tree
{
	int o[S << 2];
	void build(int l, int r, int rt)
	{
		if(l == r) { scanf("%lld", &o[rt]); return; }
		int mid = l + r >> 1;
		build(l, mid, rt << 1); build(mid + 1, r, rt << 1 | 1);
		o[rt] = o[rt << 1] + o[rt << 1 | 1];
	}
	void modify(int l, int r, int rt, int dep, int p, int v)
	{
		if(l == r) { o[rt] = v; return; }
		int mid = l + r >> 1;
		if(p <= mid) modify(l, mid, rt << 1 | rev[dep], dep - 1, p, v);
		else modify(mid + 1, r, rt << 1 | rev[dep] ^ 1, dep - 1, p, v);
		o[rt] = o[rt << 1] + o[rt << 1 | 1];
	}
	int query(int l, int r, int rt, int dep, int ql, int qr)
	{
		if(ql <= l && r <= qr) return o[rt];
		int mid = l + r >> 1, res = 0;
		if(ql <= mid) res += query(l, mid, rt << 1 | rev[dep], dep - 1, ql, qr);
		if(qr > mid) res += query(mid + 1, r, rt << 1 | rev[dep] ^ 1, dep - 1, ql, qr);
		return res;
	}
} sgt;
#define Replace(x, k) sgt.modify(1, 1 << n, 1, n, x, k)
#define Sum(l, r) sgt.query(1, 1 << n, 1, n, l, r)
#define Swap(k) rev[k + 1] ^= 1
inline void Reverse(int k) { for(int i = 0; i <= k; i++) rev[i] ^= 1; }
signed main()
{
	scanf("%lld %lld", &n, &q);
	sgt.build(1, 1 << n, 1);
	while(q--)
	{
		int opt, x, y;
		scanf("%lld", &opt);
		switch(opt)
		{
			case 1:
				scanf("%lld %lld", &x, &y); Replace(x, y); break;
			case 2:
				scanf("%lld", &x); Reverse(x); break;
			case 3:
				scanf("%lld", &x); Swap(x); break;
			case 4:
				scanf("%lld %lld", &x, &y); printf("%lld\n", Sum(x, y)); break;
		}
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM