NOI.AC#2007-light【根号分治】
生活随笔
收集整理的這篇文章主要介紹了
NOI.AC#2007-light【根号分治】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
正題
題目鏈接:http://noi.ac/problem/2007
題目大意
nnn個格子排成一排,每個格子有一個0/10/10/1和一個顏色。開始每個格子都是000,qqq次操作取反一個顏色的所有格子的0/10/10/1,然后詢問111的格子構成的連通塊數量。
1≤n,q≤1051\leq n,q\leq 10^51≤n,q≤105
解題思路
可以理解為總共的111格子數減去相鄰的111格子對數。
轉換一下模型,每隊相鄰的顏色x,yx,yx,y之間連接一條邊。
現在問題變為了每次刪除或者加入一個點,求連通子圖的邊的數量。
那么每次加入一個點xxx的時間復雜度是O(degx)O(deg_x)O(degx?),這其實是有大量重復的,因為有一些點沒有被加入但是也需要判斷。
考慮平衡一下復雜度,發現對于一條邊連接x,yx,yx,y,我們可以選擇一個點在這個點修改的時候進行處理,若兩個點的度數都在m\sqrt mm?以內那么隨便那個點處理這條邊的情況就好了。若其中有一個點的度數大于m\sqrt mm?的話,那么度數小的那個點處理。
然后兩個點的度數都大于m\sqrt mm?的話怎么辦?不難發現這些點的數量不會超過m\sqrt mm?,我們將重邊壓縮然后隨便那個點處理都可以。
這樣均攤下來時間復雜度O(qn)O(q\sqrt n)O(qn?)
code
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<cmath> using namespace std; const int N=1e5+10; int l,m,n,Q,w[N],c[N],deg[N],ans; bool v[N],z[N];vector<int>e[N]; int main() {scanf("%d%d%d",&l,&n,&Q);for(int i=1;i<=l;i++){scanf("%d",&c[i]);w[c[i]]+=1-(c[i]==c[i-1]);if(c[i]!=c[i-1])deg[c[i]]++,deg[c[i-1]]++,m+=2;}int T=sqrt(m);for(int i=1;i<=n;i++)z[i]=(deg[i]>T);for(int i=2;i<=l;i++){if(c[i]!=c[i-1]){int x=c[i],y=c[i-1];if(!z[x])e[x].push_back(y);else e[y].push_back(x);}}while(Q--){int x;scanf("%d",&x);int f=v[x]?-1:1;v[x]^=1;ans+=w[x]*f;for(int i=0;i<e[x].size();i++){int y=e[x][i];w[y]-=f;if(v[y])ans-=f;}printf("%d\n",ans);}return 0; }總結
以上是生活随笔為你收集整理的NOI.AC#2007-light【根号分治】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网页制作怎么赚钱(网页制作怎么赚钱的)
- 下一篇: C#相关基础知识点总结+基础代码