線段樹教做人系列 3 HDU 4913

2022-06-28 04:54:09 字數 2685 閱讀 5796

題意及思路看這篇部落格就行了,講得很詳細。

下面是我自己的理解:

如果只有2,沒有3的話,做法就很簡單了,只需要對陣列排個序,然後從小到大列舉最大的那個數。那麼它對答案的貢獻為(假設這個數排序後的位置是pos)2 ^ (pos - 1) * 2 ^ a[pos]。意思是a[pos]這個數必選,其它比它小的數可選可不選,有2^(pos - 1)種情況。現在相當於變成了乙個二維的問題。對於這種問題,我們常見的做法是確定一維,在從前往後掃瞄某一維時加上另一維對答案的貢獻。對於這個題,我們可以按陣列b從小到大排序,去計算a的貢獻。假設現在掃瞄到的第pos個位置(二元組(a[i], b[i])已經按陣列b排序),我們考慮來計算a[i]對答案的貢獻。a對答案的貢獻分為2部分,一部分是之前已經出現過的,小於等於a[i]的值,假設一共有x個,那麼這部分的貢獻為(2 ^ x * 2 ^ a[i]),那麼大於a[i]的部分呢?其實和這個式子差不多。對於每個已經出現過,並且大於a[i]的a[j],假設已經出現過的比a[j]小的數有y個,那麼貢獻為2 ^ (y - 1) * 2 * a[j]。為什麼是y - 1? 因為a[i]是必選的。通過觀察,我們可以發現,每乙個a[j]對答案的貢獻,取決當前已經出現過的數中有多少個比它小的數,所以我們可以這樣維護:在每次插入乙個值時,先詢問在這個數之前出現了多少個數(假設有x個),然後插入2 ^ x * 2 ^ a[i],詢問[i,n]的區間和,就是這一階段的答案。之後,要把[i + 1,n]中的數乘2,因為他們的前面都多了乙個a[i]。

**:

#include#define ls(x) (x << 1)

#define rs(x) ((x << 1) | 1)

#define ll long long

using namespace std;

const int maxn = 100010;

const ll mod = 1000000007;

struct node;

bool cmp1(node x, node y)

bool cmp2(node x, node y)

node a[maxn];

struct segementtree ;

segementtree tr[maxn * 4];

ll qpow(ll x, ll y)

return ans;

}void pushup(int x)

void maintain(int x, int y)

void pushdown(int x)

}void build(int x, int l, int r)

int mid = (l + r) >> 1;

build(ls(x), l, mid);

build(rs(x), mid + 1, r);

pushup(x);

}void update_cnt(int x, int l, int r, int pos, int y, int z)

pushdown(x);

int mid = (l + r) >> 1;

if(pos <= mid) update_cnt(ls(x), l, mid, pos, y, z);

else update_cnt(rs(x), mid + 1, r, pos ,y, z);

pushup(x);

}void update_sum(int x, int l, int r, int ql, int qr)

pushdown(x);

int mid = (l + r) >> 1;

if(ql <= mid) update_sum(ls(x), l, mid, ql, qr);

if(qr > mid) update_sum(rs(x), mid + 1, r, ql, qr);

pushup(x);

}ll query_cnt(int x, int l, int r, int ql, int qr)

int mid = (l + r) >> 1;

pushdown(x);

ll ans = 0;

if(ql <= mid) ans += query_cnt(ls(x), l, mid, ql, qr);

if(qr > mid) ans += query_cnt(rs(x), mid + 1, r, ql, qr);

return ans;

}ll query_sum(int x, int l, int r, int ql, int qr)

int mid = (l + r) >> 1;

ll ans = 0;

pushdown(x);

if(ql <= mid) ans += query_sum(ls(x), l, mid, ql, qr);

if(qr > mid) ans += query_sum(rs(x), mid + 1, r, ql, qr);

return ans % mod;

}int main()

sort(a + 1, a + 1 + n, cmp1);

for (int i = 1; i <= n; i++)

sort(a + 1, a + 1 + n, cmp2);

build(1, 1, n);

ll ans = 0;

for (int i = 1; i <= n; i++)

printf("%lld\n", ans);

}}

學習筆記 線段樹系列

把乙個陣列存成樹形結構,可以處理所有能進行區間合併的操作,單次修改和查詢為 o log n 學會使用懶標記,放個板子。支援單點和區間的加法與乘法,單點查詢和區間查詢最值與求和。int n,q ll a maxn struct segmenttree inline void push down int...

線段樹練習3

給定一條長度為m的線段,有n個操作,每個操作有3個數字x,y,z表示把區間 x,y 染成顏色z。規定 線段的顏色可以相同。連續的相同顏色被視作一段。詢問染色後的線段被分為多少段。線段樹 cover 1表示該區間由多種顏色組成。cover 0表示該區間只有一種單一的顏色cover。和線段樹練習2差不多...

復健3 線段樹

哦上帝啊,如果我有罪,請叫法律來制裁我,而不是寫了個線段樹調好幾天,這篇復健鴿了兩三天了 謝罪 因為最近在和學長聊天orz 我是什麼臭魚爛蝦我這就爬 個人覺得線段樹的思想比樹狀陣列要簡單些,但是 量確實是樹狀陣列更優美orz 樹狀陣列是從最基礎元素開始標為1,用二進位制整數下標存字首和,但是線段樹的...