<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      算法筆記(4)莫隊算法入門

      原發表于我的博客

      前言

      本來想學完回滾莫隊、樹上莫隊、二離莫隊之后一起寫一個博客,但是一直學不會/kk,只好把已會的普通莫隊和帶修莫隊寫了(以后會補上的)

      普通莫隊

      莫隊——優雅的暴力

      莫隊算法的引入

      例題

      給定一個數列和若干詢問,每次詢問詢問一段區間內不同種類數字的個數。

      暴力做法

      每次詢問暴力枚舉區間\([l,r]\),用一個數組\(cnt\)記錄每個數在區間內出現的次數,最后枚舉\(cnt\)數組,記錄所有不為空的數的個數。

      時間復雜度大概是\(O(q(n^2+m))\),其中\(m\)為數據范圍,一定會TLE的。

      改進做法1(依然暴力)

      考慮到優化,在記錄每次枚舉區間的時候,記錄下不同數的個數,就剩下了最后枚舉數據范圍的步驟,時間復雜度變成\(O(n^2q)\)

      但是依然會TLE

      改進做法2(離線力)

      考慮離線,把所有操作都讀入。

      從第一個查詢操作開始,每次以\(O(1)\)的時間復雜度拓展。

      如第一個查詢操作為\([l,r]\),第二個操作查詢\([l-3,r+4]\),那我們就一步一步地把\(l\),向左移動,每次移動的時候把拓展的數字加入cnt,更新答案,\(r\)同理。

      形象的理解就是維護兩個指針\(l,r\),每次\(l,r\)左右移動的同時更新答案。

      但是這種做法本質上還是\(O(n^2q)\)的(常數會小很多),所以我們需要繼續優化。

      改進做法3(排序大法好)

      上一個做法的低效之處在于,如果第一個操作數列后面,第二個操作在最前面,\(l\)指針就會從末尾一路走到最前面(漂洋過海來看你?),如果出題人這樣構造數據的話,復雜度就和最開始的暴力沒有區別了。

      考慮到排序,按照\(l\)排序,讓\(r\)亂蹦跶,兩個指針有序地向后竄,時間復雜度就會變成\(O(qn \log n)\)

      依然會TLE

      改進做法4(莫隊算法!!!)

      通過逐步的優化,我們已經摸索出了真正的莫隊算法!!!

      莫隊算法其實就是通過某種神奇優化,把復雜度降低(莫隊orz),這種排序方法就很神奇(我也不會證)

      考慮分塊,把數列分成\(\sqrt{n}\)塊。

      優化上一個做法,排序時,第一個關鍵字為左端點所在塊編號,第二個關鍵字是右端點編號。

      這種排序方式會把算法優化到\(O(n\sqrt{n})\)(沒法再優化了!!!)

      具體證明我也不會,可以看link

      那么代碼我們也能寫出來了。

      代碼

      const int N=1e6+10;
      int cnt[N],a[N],ans=0;
      struct node{
      	int l,r,id,ans;
      }que[N];
      int T;
      int Ans[N];
      bool cmp(node a,node b){
      	if(a.l/T==b.l/T) return a.r<b.r;//第二個關鍵字按照右端點編號排序
      	return a.l/T<b.l/T;//第一個關鍵字按照左端點所在塊編號排序
      }
      void add(int x){
      	//拓展一個數
      	if(!cnt[x]) ans++;//若這個數還沒有過,更新答案
      	cnt[x]++;
      }
      void del(int x){
      	cnt[x]--;
      	if(!cnt[x]) ans--;
      }
      int n,m;
      int main(){
      	cin>>n;
      	T=sqrt(n);//分塊
      	for(int i=1;i<=n;i++) cin>>a[i];
      	cin>>m;
      	for(int i=1;i<=m;i++) cin>>que[i].l>>que[i].r,que[i].id=i;
      	sort(que+1,que+1+m,cmp);//排序
      	int l=1,r=0;//最開始的lr指針應該是個空區間
      	for(int i=1;i<=m;i++){
      		//分別拓展
      		while(r<que[i].r) add(a[++r]);
      		while(r>que[i].r) del(a[r--]);
      		while(l>que[i].l) add(a[--l]);
      		while(l<que[i].l) del(a[l++]);
      		Ans[que[i].id]=ans;//記錄答案
      	}
      	for(int i=1;i<=m;i++) cout<<Ans[i]<<endl;
      }
      

      這個代碼其實就是洛谷P1972 HH的項鏈,但由于某洛谷知名管理員加強了數據,這題莫隊就過不去了(親測莫隊能卡到48,聽說有人卡到了92)。

      奇偶性優化

      如果\(l\)在奇數塊,就按照\(r\)順序排序,否則按照\(r\)逆序排序。

      inline bool cmp(Que a,Que b){
      	if(a.l/T!=b.l/T) return a.l/T<b.l/T;
      	if((a.l/T)&1) return a.r<b.r;
      	return a.r>b.r;
      }
      

      這個優化很有用,但是一般只會在卡常的時候用到。

      例題1:小B的詢問

      原題

      解題思路

      在HH的項鏈被卡了后,這題就算是莫隊模板了。

      思路:用一個\(cnt\)數組維護區間內數字個數,然后加減時答案增刪平方,剩下就是普通莫隊了。

      AC代碼

      #include <bits/stdc++.h>
      using namespace std;
      const int N=5e4+10;
      int cnt[N],a[N],ans=0;
      struct Que{
      	int l,r,id;//查詢結構題
      }que[N];
      int T,Ans[N];
      bool cmp(Que a,Que b){
      	if(a.l/T!=b.l/T) return a.l/T<b.l/T;//第一個關鍵字為左端點所在塊編號
      	return a.r<b.r;//第二個關鍵字為右端點編號
      }
      void add(int x){
      	ans-=cnt[x]*cnt[x];
      	cnt[x]++;
      	ans+=cnt[x]*cnt[x];
      }
      void del(int x){
      	ans-=cnt[x]*cnt[x];
      	cnt[x]--;
      	ans+=cnt[x]*cnt[x];
      }
      int n,m,k;
      int main(){
      	cin>>n>>m>>k;
      	T=sqrt(n);
      	for(int i=1;i<=n;i++) cin>>a[i];
      	for(int i=1;i<=m;i++) cin>>que[i].l>>que[i].r,que[i].id=i;//離線讀入詢問
      	sort(que+1,que+1+m,cmp);//先排序
      	int l=1,r=0;//起始lr指針設為一個空區間
      	for(int i=1;i<=m;i++){
      		//莫隊算法
      		while(r<que[i].r) add(a[++r]);
      		while(r>que[i].r) del(a[r--]);
      		while(l>que[i].l) add(a[--l]);
      		while(l<que[i].l) del(a[l++]);
      		Ans[que[i].id]=ans;//記錄答案
      	}
      	for(int i=1;i<=m;i++) cout<<Ans[i]<<endl;
      }
      

      帶修莫隊

      帶修改的莫隊就是在莫隊的基礎上多了一個修改操作(廢話)

      如果說普通的莫隊是從\([l_1,r_1]\)轉移到\([l_2,r_2]\),那帶修改的莫隊就是從\([l_1,r_1,t_1]\)轉移到\([l_2,r_2,t_2]\)

      所以我們可以把修改操作也存下來,執行莫隊算法的時候,先按照普通莫隊的做法轉移到下一個區間,然后再進行(或撤銷)這兩次查詢之間進行的修改,就相當于轉移到了目標的詢問。

      可以把帶修莫隊理解成一個多了時間的坐標軸,如圖

      帶修莫隊

      至于實現,則比較簡單,排序時以左端點所在塊編號為第一個關鍵字,右端點所在塊編號為第二個關鍵字,第三個關鍵字是時間戳(在這次查詢前右多少次修改)。

      普通莫隊維護兩個指針\(l,r\),帶修莫隊多維護一個指針\(t\)即可。

      另外分塊時不能以\(\sqrt{n}\)分了,帶修莫隊一般一塊\(n^{\frac{2}{3}}\),分成\(n^{\frac{1}{3}}\)塊,復雜度為\(O(n^{\frac{3}{5}})\)(不會證明,可以看oiwiki

      例題2:數顏色

      原題

      帶修莫隊模板題。

      AC代碼

      #include <bits/stdc++.h>
      using namespace std;
      const int N=1e6+10;
      int T;
      struct Que{
      	int l,r,id,tim;
      }que[N];
      int as[N];
      bool cmp(Que a,Que b){
      	if(a.l/T==b.l/T){
      		if(a.r/T==b.r/T) return a.tim<b.tim;
      		return a.r/T<b.r/T;
      	}
      	return a.l/T<b.l/T;
      }
      struct Upd{
      	int p,col;
      }upd[N];
      int cnt[N],ans=0,a[N];
      void add(int x){
      	if(!cnt[x]) ans++;
      	cnt[x]++;
      }
      void del(int x){
      	cnt[x]--;
      	if(!cnt[x]) ans--;
      }
      int n,m,iq=0,ic=0;
      int main(){
      	cin>>n>>m;
      	for(int i=1;i<=n;i++) cin>>a[i];
      	for(int i=1;i<=m;i++){
      		char opt;
      		int l,r;
      		cin>>opt>>l>>r;
      		if(opt=='Q') que[++iq]={l,r,iq,ic};
      		else upd[++ic]={l,r};
      	}
      	T=pow(n,0.666);//即n^2/3
      	sort(que+1,que+1+iq,cmp);//排序
      	int l=1,r=0,now=0;//三個指針
      	for(int i=1;i<=m;i++){
      		//這部分和普通莫隊差不多
      		while(l<que[i].l) del(a[l++]);
      		while(l>que[i].l) add(a[--l]);
      		while(r>que[i].r) del(a[r--]);
      		while(r<que[i].r) add(a[++r]);
      		//轉移到新詢問的時間戳
      		while(now<que[i].tim){
      			++now;
      			int p=upd[now].p,c=upd[now].col;
      			if(l<=p&&p<=r) del(a[p]),add(c);
      			swap(a[p],upd[now].col);
      		}
      		while(now>que[i].tim){
      			int p=upd[now].p,c=upd[now].col;
      			if(l<=p&&p<=r)	del(a[p]),add(c);
      			swap(a[p],upd[now].col);
      			now--;
      		}
      		as[que[i].id]=ans;//記錄答案
      	}
      	for(int i=1;i<=iq;i++) cout<<as[i]<<endl;
      }
      
      posted @ 2023-10-23 18:09  烈風光翼  閱讀(82)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 精品国产亚洲区久久露脸| 国产日韩av二区三区| 成人网站国产在线视频内射视频 | 人妻av无码系列一区二区三区| 日韩亚洲国产中文字幕欧美| 蜜臀av一区二区三区在线| 国产99在线 | 免费| 天堂亚洲免费视频| 亚洲精品无码日韩国产不卡av| 国产一区二区三区不卡视频| 久久亚洲精品情侣| 一区二区偷拍美女撒尿视频 | 图片区偷拍区小说区五月| 中文字幕久久精品波多野结| 久久亚洲精品成人av无| 国产精品三级在线观看无码| 无码一区二区三区视频| 成人免费A级毛片无码片2022| 国产suv精品一区二区33| 男人和女人做爽爽免费视频| 国产MD视频一区二区三区| 好爽毛片一区二区三区四| 国产精品国产精品偷麻豆| 亚洲中文字幕综合小综合| 亚洲国产成人AⅤ片在线观看| 国产精品一区二区无线| 亚洲一区av在线观看| 国产精品区一二三四久久| 国产精品乱一区二区三区| 狠狠躁夜夜躁人人爽天天5| 久久精品国产99国产精品严洲| 国产午夜精品福利免费看| 中文幕无线码中文字夫妻| 四虎永久精品在线视频| 无码日韩精品91超碰| 中文国产日韩欧美二视频| 国产美女免费永久无遮挡| 天长市| 日本韩国日韩少妇熟女少妇| 丝袜人妻一区二区三区网站| 扒开粉嫩的小缝隙喷白浆视频|