1. 程式人生 > >序列算法

序列算法

取值 區間 技術分享 離散化 分享 urn 離散 多次 很多

區間查詢&單點修改:

給定一個序列a,進行很多次操作:
訪問a[l ~~ r]的區間和;
將a[i] 的值修改為 a[i] + k;


求區間x ~~ y中的區間和:

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 #define MAXN 2222
 5 
 6 int n, m;
 7 int a[MAXN], s[MAXN];
 8 
 9 int main() {
10     scanf("%d%d", &n, &m);
11     for(int i = 1
; i <= n; i++) { 12 13 scanf("%d", &a[i]); 14 s[i] = s[i - 1] + a[i]; //前綴和 15 } 16 for(int i = 1, x, y; i <= m; i++) {//m次操作 17 scanf("%d%d", &x, &y); 18 printf("%d\n", s[y] - s[x - 1]);//s[y] - s[x - 1]即為x ~~ y的區間和 19 } 20 return
0; 21 } 22 /* 23 10 100 24 1 9 5 6 8 7 4 3 6 9 25 */

樹狀數組:

**/*樹狀數組:動態維護前綴和*/**

技術分享圖片

 1 #include<cstdio>
 2 
 3 #define lowbit(x) (x & -x)
 4 #define MAX 11111
 5                       int n,m;
 6                       int t[MAX],a[MAX];//t為樹狀數組
 7 
 8 void add(int x, int k) {//單點修改(向樹上走)
9 for(int i = x; i <= n; i += lowbit(i)) { 10 t[i] += k; 11 } 12 } 13 14 int query(int x) {//區間查詢(在樹下向前推進)//求的是前綴和 15 int sum = 0; 16 for(int i = x; i; i -= lowbit(i)) { 17 sum += t[i]; 18 } 19 return sum; 20 } 21 22 void lineplus(const int & l , const int &r, const int &k) { 23 add(l , k); 24 add(r + 1 , -k); 25 } //區間加 & 單點查詢 26 27 int main() { 28 scanf("%d%d",&n,&m); 29 for(int i = 1; i <= n; i++) { 30 scanf("%d",&a[i]); 31 add(i , a[i]); 32 } 33 int x,y; 34 /*for(int i = 1; i <= m; i++) { 35 scanf("%d%d",&x,&y); 36 printf("%d\n",query(y) - query(x - 1));//求的是區間和(x~~y) 37 }*/ 38 int k; 39 scanf("%d%d%d",&x,&y,&k); 40 lineplus(x,y,k);//x~~y區間加k 41 } 42 43 ``` 44 /* 45 5 100 46 1 4 5 6 3 47 1 48 ans:1 49 */

逆序對:

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 #define MAXN 222222
 5 
 6 using namespace std;
 7 
 8 int n, ans, tot;
 9 int a[MAXN], b[MAXN], t[MAXN];//t為樹狀數組,b用來離散化a
10 
11 int lowbit(int x) {
12     return x & -x;
13 }
14 
15 int query(int x) {
16     int s = 0;
17     for(int i = x; i; i -= lowbit(i)) s += t[i];
18     return s;
19 }//區間查詢 返回的是前綴和
20 
21 void add(int x) {
22     for(int i = x; i <= n; i += lowbit(i)) t[i]++;
23 }
24 
25 int main() {
26     scanf("%d", &n);
27     for(int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];//b用於去重
28 
29     //離散化:將數值轉化為排名,減小取值範圍
30     sort(b + 1, b + 1 + n);//先升序排個序
31     tot = 1;//第一個是不用去重的
32     printf("去重前 ");
33     for(int i = 1; i <= n; i++) printf("%d ", b[i]);//
34     for(int i = 2; i <= n; i++) if(b[i] != b[tot]) b[++tot] = b[i];//去重 ,從第二個開始
35     //補:unique()函數是一個去重函數,功能是去除相鄰的重復元素(只保留一個)
36     //unique(num,mun+n)返回的是num去重後的尾地址,
37     printf("\n去重後 ");
38     for(int i = 1; i <= tot; i++) printf("%d ", b[i]);
39     for(int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + 1 + tot, a[i]) - b;
40     //lower_bound(b + 1, b + 1 + tot, a[i])是在區間b + 1~~~b + 1 + tot中找到第一個大於等於a[i]的數,並返回它的地址
41     //所以要“- b”減b ,之後,返回的是值等於a[i]的下標(即排名) 因為b一定有a[i]  所以它就是找第一個等於a[i]的數
42     printf("\n離散化a  ");
43     for(int i = 1; i <= n; i++) printf("%d ", a[i]);
44 
45     for(int i = 1; i <= n; i++) {
46         add(a[i]);
47         int tmp = query(a[i]);
48         tmp = i - tmp;//tmp一開始是i之前的  大於i的 數 的 個數
49         ans += tmp;//逆序對的個數的和
50     }
51     printf("\n%d\n", ans);
52     return 0;
53 }
54 
55 ```
56 
57 /*
58 7
59 6 2 5 1 3 7 4
60 7
61 33 23 17 999 5 17 999
62 */

前綴和(二維):

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 
 5 #define MAXN 1111
 6 #define MAXM 1111
 7 
 8 int n,m;//長&寬
 9 int a[MAXN][MAXM],s[MAXN][MAXM];
10 /*a為矩陣每個點的值,s[i][j]表示 以[i,j]為右下角的矩陣中(左上角為(0,0))
11 所有方格中數的和
12 */
13 int x1,y1,x2,y2;
14 /*詢問的是以(x1,y1)為左上 以(x2,y2)為右下的矩陣中
15 所有方格中數的和
16 */
17 
18 int main() {
19     scanf("%d%d",&n,&m);
20     for(int i = 1; i <= n; i++) {
21         for(int j = 1; j <= m; j++) {
22             scanf("%d",&a[i][j]);
23         }
24     }
25     //初始化:
26     for(int i = 1; i <= n; i++) {
27         for(int j = 1; j <= m; j++) {
28             s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
29             //        (都是已知的)           s[i - 1][j - 1]為重復加的  然後加上他自己
30         }
31     }
32     scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
33     printf("%d",s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1]);
34     // s[x1 - 1][y1 - 1] 重復減的
35     //溫馨提示:畫個矩陣唄
36 }

區間加:

給定一個序列a(初值全為0)。有很多次操作:
將a[I ~~ j]的每個值加上k
詢問a[x]的值
(實際上是區間修改,單點查詢問題,不過可以用差分數組改成區間查詢(求p[i]的前綴和)單點修改(修改p[l],p[r + 1]的值)
差分數組的前綴和即為原數組,修改同步

代碼:

 1 #include<cstdio>
 2            /* 5   2  4  4  3
 3            p:5  -3 -2  0  -1 */
 4 #define MAXN 1111
 5            int n,m;//給定序列a的長度為n,m次操作
 6            int a[MAXN],p[MAXN];//p 表示a中相鄰元素的差 例:p[i] = a[i] - a[i - 1]
 7 //                 即差分
 8            int l,r,k;
 9 
10 void add(int l, int r, int k) { //加k的區間是【l , r】
11     p[l] += k;//p受影響的只有l 和 r+1 因為a[r + 1]值不變,所以p[r + 2]不變
12     p[r + 1] -= k;//只需要修改p[l] 和 p[r + 1] 的值,然後求一個前綴和就行了
13 }
14 
15 void get_a() {
16     for(int i = 1; i<= n; i++) {
17         a[i] = a[i - 1] + p[i];//相當於一維前綴和
18     }
19 }
20 
21 int main() {
22     scanf("%d%d",&n,&m);
23     for(int i = 1; i <=m; i++) {
24         scanf("%d %d %d",&l,&r,&k);
25         add(l,r,k);
26     }
27     get_a();
28     for(int i = 1; i <=n; i++) {
29         printf("%d ",a[i]);
30     }
31 }

序列算法