1. 程式人生 > 其它 >彈性盒子佈局

彈性盒子佈局

目錄

  1. 前言
  2. 樹狀陣列基礎
    2.1 定義
    2.2 lowbit
    2.3 部分性質
    2.4 單點加,區間加,求字首和
  3. 樹狀陣列求逆序對

1.前言

關於我線段樹合併都會了還不懂樹狀陣列這回事

2.樹狀陣列基礎

2.1 樹狀陣列定義

眾所周知,世界上有個東西叫線段樹,維護的是一個區間。
它大概長這樣:

而把它移一下,就是樹狀陣列

易得
\(c_1=a_1\)
\(c_2=a_1+a_2\)
\(c_3=a_3\)
\(c_4=a_1+a_2+a_3+a_4\)
\(......\)
這樣看可能發現不了什麼規律

2.2 lowbit

定義一個函式 \(f=lowbit(x)\) ,表示 \(x\) 在二進位制表示式中最低位 \(1\)

所表示的數
舉個例子:

\[(10)_2=1010 \]

最低位 \(1\) 在倒數第二位,所以

\[lowbit(10)=(10)_{10}=2 \]

所以易得:
\(k\) 為一個二進位制數最低位 \(1\) 所在的位數,所以這個數的lowbit值即為 \(2^{k-1}\)
所以這個lowbit值可以用以下程式碼求:

int lowbit(int x)
{
    return x&(-x);
}

那麼這個lowbit有什麼用呢?
我們再把之前的樹狀陣列搬過來
\(c_1=a_1\)
\(c_2=a_1+a_2\)
\(c_3=a_3\)
\(c_4=a_1+a_2+a_3+a_4\)


\(......\)
如果我們把下標變成2進位制
\(c_{0001}=a_{0001}\)
\(c_{0010}=a_{0001}+a_{0010}\)
\(c_{0011}=a_{0011}\)
\(c_{0100}=a_{0001}+a_{0010}+a_{0011}+a_{0100}\)
\(......\)
\(k\)\(i\) 在二進位制表示式中最低位 \(1\) 所表示的數
不難發現:

\[c_i=a_{i-2^{k-1}+1}+a_{i-2^{k-1}+2}+...+a_i \]

即:

\[c_i=a_{i-lowbit(i)+1}+a_{i-lowbit(i)+2}+...+a_i \]

這就是 \(c\) 陣列的規律

2.3 部分性質

  1. \(c_x\) 儲存以它為根的子樹中所有葉節點的個數
  2. \(c_x\) 的子節點個數為 \(lowbit(x)-1\)
  3. 除樹根外,\(c_x\) 的父節點為 \(c_{x+lowbit(x)}\)
  4. 樹的深度為 \(\text{log}\)\(_2n\)

2.4 單點加,區間加,區間查詢

由於我們知道了 \(c_x\) 的子節點個數為 \(lowbit(x)-1\) 和 除樹根外,\(c_x\) 的父節點為 \(c_{x+lowbit(x)}\) 這兩個性質,所以以下的就很好理解了
單點加,區間查詢
因為樹狀陣列維護的是字首和,所以一個點改變肯定會影響到它的父節點
我們知道 \(c_x\)的父節點是 \(c_{x+lowbit(x)}\) ,所以我們每次跳 \(lowbit(x)\) ,也就是跳到 \(x\) 的父節點,然後將它的值加上要加的值就可以了
而區間查詢也很好解決,因為樹狀陣列本來就是維護字首和的,所以如果查詢 \([x,y]\) 的和,就是查詢 \([1,y]-[1,x-1]\)
程式碼:

int lowbit(int x)
{
	return x&(-x);
}
void add(int x,int k)//單點加
{
	for(;x<=n;x+=lowbit(x))
		c[x]+=k;
}
int sum(int x)//區間查詢
{
	int ans=0;
	for(;x;x-=lowbit)
		ans+=c[x];
	return ans;
}

區間加,單點查詢
區間點加加上單點查詢的話,我們的做法就不一樣了。因為如果用之前的做法,那肯定會超時
所以我們就要用到差分的思想,利用差分建樹
我們令 \(a[0]=0,b[i]=a[i]-a[i-1]\)
易得:

\[a[i]=\sum_{j=1}^i b[j] \]

我們很容易發現,如果有區間值改變,差值是不變的,只有 \(b[x]\)\(b[y+1]\) 改變。所以我們可以用這個來建立樹狀陣列
程式碼:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e5+7;
int c[N],a[N];
int n,m;
int lowbit(int x)
{
	return x&(-x);
}
int sum(int x)
{
	int ans=0;
	for(;x;x-=lowbit(x))
		ans+=c[x];
	return ans;
}
void add(int x,int k)
{
	for(;x<=n;x+=lowbit(x)) c[x]+=k;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	//	add(i,a[i]);
	}
	while(m--)
	{
		int op;
		cin>>op;
		if(op==1)
		{
			int x,y,k;
			cin>>x>>y>>k;
			add(x,k);
			add(y+1,-k);
		}
		else
		{
			int x;
			cin>>x;
			cout<<sum(x)+a[x]<<endl;
		}	
	}
	return 0;
}

3.樹狀陣列求逆序對

咕了,過段時間再更