序列算法
阿新 • • 發佈:2019-05-02
取值 區間 技術分享 離散化 分享 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 return0; 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 }
序列算法