1. 程式人生 > >codeforces 438D The Child and Sequence(線段樹:單點更新+區間取模+區間和)

codeforces 438D The Child and Sequence(線段樹:單點更新+區間取模+區間和)

題意:

一個n個數的序列。對它進行 3 種操作。
  1 l r:輸入a[l,r]的和
  2 l r x:令[l,r]所有數對x取模
  3 k x:令a[k] = x

每到操作1時輸出和。

(1 ≤ n, m ≤ 1e5).  (1 ≤ a[i] ≤ 1e9) 

分析:

看到區間更新,應該想到懶惰標記。但是用懶惰標記應該滿足兩個條件:

  1. 標記可以合併
  2. 可以快速更新區間資訊。

但是這道題不滿足條件2,也就是無法快速更新區間和。而逐個更新每個數取模,彷彿又太慢了。

正確解法基於一個重要的結論,a % b <= a/2,這就意味著,任何一個數loga[i]次取模以內就能變為0。同時還注意到,a % b = a (a < b)。這兩點便可以極大的降低時間複雜度。

至此,便得到解題思路。單點更新和區間和並無特殊之處,而對於每個區間取模操作,如同遍歷這個區間所有的數字一樣,逐個取模更新。但是若某個區間的區間最大值小於mod,那麼這個區間就無需有任何操作。這相當於一個剪枝。所以我們需要兩個陣列,一個記錄區間和,一個記錄區間最大值。總的時間複雜度為O(nlognloga[i])。

其實線段樹的實質就是一個dfs搜尋樹的過程,只不過在每次搜尋的時候都判斷區間是否重合、是否相交、區間某值是否滿足條件等等,這些過程的實質就是剪枝。時間複雜度就是靠這些剪枝降到logn的,否則就像遍歷一棵樹一樣是O(n)。

程式碼

#include <iostream>
#include <algorithm> #include <queue> #include <stack> #include <vector> #include <set> #include <cmath> #include <cstdlib> #include <cstring> #include <cstdio> using namespace std; #define ms(a,b) memset(a,b,sizeof(a)) typedef long long ll; #define lson rt*2,l,(r+l)/2
#define rson rt*2+1,(l+r)/2+1,r const int MAXN = 1e5 + 5; const double EPS = 1e-8; const int INF = 0x3f3f3f3f; ll mx[MAXN << 2], sum[MAXN << 2]; int n, m; void pushup(int rt) { mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]); sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; } void build(int rt, int l, int r) { if (l == r) { scanf("%lld", &mx[rt]); sum[rt] = mx[rt]; return; } build(lson); build(rson); pushup(rt); } ll query(int L, int R, int rt, int l, int r) { if (L <= l && R >= r) { return sum[rt]; } if (R <= (l + r) / 2) return query(L, R, lson); else if (L > (l + r) / 2) return query(L, R, rson); else return query(L, R, lson) + query(L, R, rson); } void update(int L, int R, int rt, int l, int r, int mod) { if (L <= l && R >= r) { if (mx[rt] < mod) return; if (l == r) { mx[rt] = sum[rt] = mx[rt] % mod; return; } } if (L <= (l + r) / 2) update(L, R, lson, mod); if (R > (l + r) / 2) update(L, R, rson, mod); pushup(rt); } void change(int rt, int l, int r, int k, int x) { if (l == r) { mx[rt] = x; sum[rt] = x; return; } if (k <= (l + r) / 2) change(lson, k, x); else change(rson, k, x); pushup(rt); } int main() { while (~scanf("%d%d", &n, &m)) { build(1, 1, n); while (m--) { int op, L, R, k, x; scanf("%d", &op); if (op == 1) { scanf("%d%d", &L, &R); printf("%lld\n", query(L, R, 1, 1, n)); } else if (op == 2) { scanf("%d%d%d", &L, &R, &x); update(L, R, 1, 1, n, x); } else { scanf("%d%d", &k, &x); change(1, 1, n, k, x); } } } return 0; }

相關推薦

codeforces 438D The Child and Sequence(線段更新+區間+區間)

題意: 一個n個數的序列。對它進行 3 種操作。 1 l r:輸入a[l,r]的和 2 l r x:令[l,r]所有數對x取模 3 k x:令a[k] = x 每到操作1時輸出和。 (1 ≤ n, m ≤ 1e5). (1 ≤ a[i] ≤ 

CF(438D) The Child and Sequence(線段)

ria neu rom ace val author eight *** ble 題意:對數列有三種操作: Print operation l,?r. Picks should write down the value of . Modulo ope

[均攤複雜度線段]Codeforces 438D. The Child and Sequence

首先一個數模一個小於它的數,肯定會變成原來的一半 那麼用均攤複雜度線段樹的套路,記一下區間最大值,區間取模時,如果區間最大值小於模數,那麼不會改變數列,直接退出,否則暴力遞迴。 每個點最多變化log次,複雜度應該就是 O(nlog2n) #include

[均攤複雜度線段] Codeforces #438D. The Child and Sequence

水~~~ #include<cstdio> #include<algorithm> using namespace std; const int maxn=200005; typedef unsigned long long uL

Codeforces 438D The Child and Sequence

stdout 如果 scanf pri () algorithm i++ main 一個 題意:給定一個n個數的序列,完成以下3個操作:   1.給定區間求和   2.給定區間對x取模   3.單點修改 對一個數取模,這個數至少折半。於是我們記一個最大值max,如果x&

cf250D. The Child and Sequence(線段 均攤復雜度)

etc class chm har test 一個 deb 區間 如果 題意 題目鏈接 單點修改,區間mod,區間和 Sol 如果x > mod ,那麽 x % mod < x / 2 證明: 即得易見平凡, 仿照上例顯然, 留作習題答案略, 讀者自證不難。

438D The Child and Sequence線段

Time limit 4000 ms Memory limit 262144 kB At the children’s day, the child came to Picks’s house, and messed his house up. Picks wa

線段模板(更新

div ret onclick 更新 turn space view date span 1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #inc

HDU 1166 敵兵佈陣【線段更新區間求和)】

C國的死對頭A國這段時間正在進行軍事演習,所以C國間諜頭子Derek和他手下Tidy又開始忙乎了。A國在海岸線沿直線佈置了N個工兵營地,Derek和Tidy的任務就是要監視這些工兵營地的活動情況。由於採取了某種先進的監測手段,所以每個工兵營地的人數C國都掌握的一清二楚,每個工兵營地的人數都有可能發生

hdu 1166敵兵佈陣(線段 模板 更新加區間查詢)

#include<iostream> #include<cstdio> #include<cstring> #include<cstring> #include<algorithm> using namespac

線段模板-更新 區間求和(nefuoj1472)

#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long ll; #define maxn 10000

zkw(張昆瑋)線段更新

本題目包含多組測試,請處理到檔案結束。 在每個測試的第一行,有兩個正整數 N 和 M ( 0<N<=200000,0<M<5000 ),分別代表學生的數目和操作的數目。 學生ID編號分別從1編到N。 第二行包含N個整數,代表這N個學生的初始成績,其中第i個數代表ID為i的學生的成績。

線段更新與區域更新

線段樹 實在不好意思不寫了哈哈哈啊哈。 線段樹主要是解決一些大量操作區間並且維護區間的題目(維護的區間要有加和性)。 線段樹核心函式有以下幾個 pushup()(主要用來向上跟新節點資訊) pushdown()(用來向下更新來不及更新的節點) quer

CF438D The Child and Sequence線段

include efi print ems new 代碼 names for its 題目鏈接:CF原網 洛谷 題目大意:維護一個長度為 $n$ 的正整數序列 $a$,支持單點修改,區間取模,區間求和。共 $m$ 個操作。 $1\le n,m\le 10^5$。其它數

Codeforces Round #250 (Div. 1) D. The Child and Sequence 分塊

/* Codeforces Round #250 (Div. 1) D. The Child and Sequence 分塊 連結:https://codeforces.com/contest/438/problem/D 題意: op 1 : 區間加 op 2 : 區間每個數取模 op 3 :

codeforces438D The Child and Sequence

getc pre etc ack cto 個數 暴力 out 證明 題面:codeforces438D 正解:線段樹。 這題好像是$picks$出的題,然後無限弱化才上的$cf$。。 區間取模,每個數取模以後至少$/2$,所以暴力搞即可。 證明:若$p<

Codeforces 437D The Child and Zoo

最大的 limits cin 類型 Go pro type 求平均值 names Codeforces 437D The Child and Zoo 題目大意: 有一張連通圖,每個點有對應的值。定義從p點走向q點的其中一條路徑的花費為途徑點的最小值。定義f(p,q)為從點

CodeForces 438E The Child and Binary Tree(DP + 生成函式 + 多項式運算)

    大致題意:給定一個集合{Cn},一棵二叉樹上的所有節點的點權值從這個集合中選取。現在給定一個m,問對於1..m中的每一個數字i,權值和恰好為i的不同的二叉樹的個數有多少個。這裡形態不同但點權集合的二叉樹視為兩種方案。 與前面做的題目類似,

Codeforces 437C The Child and Toy(貪心)

題目大意:孩子有一個玩具,有n個部件組成,m條繩子組成,每條繩子連線兩個部件。小孩比較頑皮,要將玩具拆成不可分割的部件,每次剪斷一條繩子的代價是該繩子連線的兩個部件的權值中較小的值。問說最小的總代價是多少。 解題思路:以為每條邊都是要被剪斷的,所以將節點按照代價值從大

Codeforces 437C The Child and Toy( 貪心)

一個真正強大的人,不會把太多心思花在取悅和親附別人上面。所謂圈子、資源,都只是衍生品。最重要的是提高自己的內功。只有自己修煉好了,才會有別人來親附。自己是梧桐,鳳凰才會來棲;自己是大海,百川才來匯聚,花香自有蝶飛來。你只有到了那個層次,才會有相應的圈子,而不是倒過來!