线段树教做人系列(3) HDU 4913
題意及思路看這篇博客就行了,講得很詳細。
下面是我自己的理解:
如果只有2,沒有3的話,做法就很簡單了,只需要對數(shù)組排個序,然后從小到大枚舉最大的那個數(shù)。那么它對答案的貢獻為(假設這個數(shù)排序后的位置是pos)2 ^ (pos - 1) * 2 ^ a[pos]。意思是a[pos]這個數(shù)必選,其它比它小的數(shù)可選可不選,有2^(pos - 1)種情況。現(xiàn)在相當于變成了一個二維的問題。對于這種問題,我們常見的做法是確定一維,在從前往后掃描某一維時加上另一維對答案的貢獻。對于這個題,我們可以按數(shù)組b從小到大排序,去計算a的貢獻。假設現(xiàn)在掃描到的第pos個位置(二元組(a[i], b[i])已經(jīng)按數(shù)組b排序),我們考慮來計算a[i]對答案的貢獻。a對答案的貢獻分為2部分,一部分是之前已經(jīng)出現(xiàn)過的,小于等于a[i]的值,假設一共有x個,那么這部分的貢獻為(2 ^ x * 2 ^ a[i]),那么大于a[i]的部分呢?其實和這個式子差不多。對于每個已經(jīng)出現(xiàn)過,并且大于a[i]的a[j],假設已經(jīng)出現(xiàn)過的比a[j]小的數(shù)有y個,那么貢獻為2 ^ (y - 1) * 2 * a[j]。為什么是y - 1? 因為a[i]是必選的。通過觀察,我們可以發(fā)現(xiàn),每一個a[j]對答案的貢獻,取決當前已經(jīng)出現(xiàn)過的數(shù)中有多少個比它小的數(shù),所以我們可以這樣維護:在每次插入一個值時,先詢問在這個數(shù)之前出現(xiàn)了多少個數(shù)(假設有x個),然后插入2 ^ x * 2 ^ a[i],詢問[i,n]的區(qū)間和,就是這一階段的答案。之后,要把[i + 1,n]中的數(shù)乘2,因為他們的前面都多了一個a[i]。
代碼:
#include<bits/stdc++.h> #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{int x, y, rank; }; bool cmp1(node x, node y) {return x.x == y.x ? x.y < y.y : x.x < y.x; } bool cmp2(node x, node y) {return x.y == y.y ? x.x < y.x : x.y < y.y; } node a[maxn]; struct SegementTree {LL sum, cnt, lz; }; SegementTree tr[maxn * 4]; LL qpow(LL x, LL y) {LL ans = 1;for (; y; y >>= 1) {if(y & 1) ans = (ans * x) % mod;x = (x * x) % mod;}return ans; } void pushup(int x) {tr[x].sum = (tr[ls(x)].sum +tr[rs(x)].sum) % mod;tr[x].cnt = (tr[ls(x)].cnt + tr[rs(x)].cnt) % mod; } void maintain(int x, int y) {tr[x].sum = (tr[x].sum * qpow(2, y)) % mod;tr[x].lz += y; } void pushdown(int x) {if(tr[x].lz) {if(tr[ls(x)].cnt) maintain(ls(x), tr[x].lz);if(tr[rs(x)].cnt) maintain(rs(x), tr[x].lz);tr[x].lz = 0;} } void build(int x, int l, int r) {if(l == r) {tr[x].sum = tr[x].cnt = 0;return;}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) {if(l == r) {tr[x].cnt = 1;tr[x].sum = (qpow(2, y) * qpow(2, z)) % mod;return;}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) {if(l >= ql && r <= qr) {tr[x].lz++;tr[x].sum = (tr[x].sum * 2) % mod;return;}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) {if(l >= ql && r <= qr) {return tr[x].cnt;}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) {if(l >= ql && r <= qr) {return tr[x].sum;}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() {int n;while(~scanf("%d", &n)) {for (int i = 1; i <= n; i++) {scanf("%d%d", &a[i].x, &a[i].y);}sort(a + 1, a + 1 + n, cmp1);for (int i = 1; i <= n; i++) {a[i].rank = i;}sort(a + 1, a + 1 + n, cmp2);build(1, 1, n);LL ans = 0;for (int i = 1; i <= n; i++) {LL tmp = query_cnt(1, 1, n, 1, a[i].rank);update_cnt(1, 1, n, a[i].rank, tmp, a[i].x);ans = (ans + query_sum(1, 1, n, a[i].rank, n) * qpow(3, a[i].y) % mod) % mod;if(a[i].rank != n)update_sum(1, 1, n, a[i].rank + 1, n);}printf("%lld\n", ans);} }
轉載于:https://www.cnblogs.com/pkgunboat/p/10550737.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的线段树教做人系列(3) HDU 4913的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HNOI2013 游走
- 下一篇: codeforces 922E