五個小技巧node
a爲原數組,從第一項開始數組
前綴和顧名思義,前面的和,具體來講就是前n項的和
sum爲前綴和數組spa
第n項的前綴和等於第n-1項前綴和+第i項數之和
\(\sum_{1}^{n}a[i]=\sum_{1}^{n-1}a[i]+a[i]\)指針
sum[i]=sum[i-1]+a[i];
應用
求靜態區間和
例如求 a[i]區間[l,r]的和
\(\sum_{i=l}^{r}a[i]=\sum_{i=1}^{r}a[i]-\sum_{i=1}^{l-1}a[i]\)code
\(\sum_{i=1}^{n}\sum_{j=1}^{n}a[i][j]=\sum_{i=1}^{n-1}\sum_{j=1}^{n}a[i][j]+\sum_{i=1}^{n}\sum_{j=1}^{n-1}a[i][j]+a[n][n]-\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}a[i][j]\)
(至於怎麼推出來的,建議自行畫個圖表示一下)blog
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]
應用與一維相似排序
差分 爲 相差的數隊列
定義cf[n]爲差分序列
\(cf[i]=a[i]-a[i-1]\)
應用
區間求加數後的第i個數
例如一個區間[l,r]都加上一個數x,求第i個數
求第n個數,設第i個數爲y
\(y=\sum_{i=1}^{n}cf[i]\)ip
當[l,r]加上一個數x,cf[l+1,r]都不會發生改變
只有\(cf[l]=a[l]+x-a[l-1]=cf[l]+x,cf[r+1]=a[r+1]-(a[r]+x)=cf[r+1]-x\)get
\(a[i]=sum[i]-sum[i-1]\)
\(cf[i]=a[i]-a[i-1]\)
這裏借鑑一維的變化
推出二維的差分
(爲何能推出來,由於前綴和的差分就是原數列,又由於差分的前綴和是原數列,因此由前綴和差分獲得的原序列一樣適用於由原序列差分獲得差分序列)
\(a[i][j]=sum[i][j]-sum[i-1][j]-sum[i][j-1]+sum[i-1][j-1]\)
\(cf[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]\)
應用與一維相似
至於與一位的差別
好比給以\(x_1,y_1\)爲左上角,\(x_2,y_2\)爲右下角的矩陣都加上x
此時cf數組就要進行以下操做
cf[x1][y1]+=x; cf[x1][y2+1]-=x; cf[x2+1][y1]-=x; cf[x2+1][y2+1]+=x;
依次來看
根據前綴和性質
\(cf[x1][y1]+=x\) 整個黃色部分在求其a[i][j]\(i>x_1 和j>y_1\)時都會因其影響
\(cf[x1][y2+1]-=x\) 同理藍色部分會消除影響
\(cf[x2+1][y1]-=x\) 同理藍色部分會消除影響
\(cf[x2+1][y2+1]+=x\) 消除兩個藍色過分消除的影響
離散化是在考慮數與數之間的大小關係而不考慮具體數值的狀況下使用
好比序列 \(1,100000,2999\)
有兩種離散化方式
這裏介紹一種
以值爲排序,用下標代替原來的值
容易知道 \(1,2999,100000\)
下標代替原來的值爲\(1,2,3\)
struct node{ int val,p; }s[maxn]; bool cmp(node a,node b){ return a.val<b.val; } sort(s+1,s+1+n,cmp);
雙指針不是c語言中的指針
而是用兩個變量遍歷
舉例就懂了,好比二分查找時的l,r 還有就是如今手寫快速排序時,左右指針找大於小於中間數的值
思路就是兩遍跑單調隊列,一遍維護單調單調遞增序列,一遍維護單調遞減序列
重點介紹怎麼維護單調隊列
單調隊列,是在隊列的基礎上,使隊列一樣具備單調性(注意區分單調隊列和優先隊列)這個單調性可根據本身定義來(最小值啊,最大值啊,其餘...
*code
#include<cstdio> using namespace std; int n,k; int a[1000000+10]; int q[1000000+10],p[1000000+10]; int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int head=1,tail=0; for(int i=1;i<=n;i++) { while(head<=tail && q[tail]>=a[i]) --tail;//找到比他小的數爲止 q[++tail]=a[i];//不然入隊列 p[tail]=i; while(p[head]<=i-k) head++;//窗口後移一次,第一個元素過期 下標比較 if(i>=k) printf("%d ",q[head]); //i大於窗口長度時循環一次輸出一次 輸出隊列最小值 } printf("\n"); head=1,tail=0;//重置隊列,再次從新從頭找最大值 for(int i=1;i<=n;i++) { while(head<=tail && q[tail]<=a[i]) tail--;//找到比他大的數爲止 q[++tail]=a[i];//不然入隊列 p[tail]=i; while(p[head]<=i-k) head++;//窗口後移一次,第一個元素過期 下標比較 if(i>=k) printf("%d ",q[head]); //i大於窗口長度時循環一次輸出一次 輸出隊列最大值 } }