專題訓練之莫隊算法
推薦博客/專欄:https://blog.csdn.net/xianhaoming/article/details/52201761莫隊算法講解(含樹上莫隊)
https://blog.csdn.net/hzj1054689699/article/details/51866615莫隊算法
https://zhuanlan.zhihu.com/p/25017840莫隊算法
例題及講解:(BZOJ2038)https://www.luogu.org/problemnew/show/P1494
講解:https://www.cnblogs.com/MashiroSky/p/5914637.html
https://blog.csdn.net/xym_csdn/article/details/50889293
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 typedef long long ll; 7 const ll maxn=5e4+10; 8 struct node{ 9 ll l,r,id,belong; 10 ll a,b; 11 }arr[maxn]; 12 ll num[maxn],a[maxn],ans; 13 14 bool cmp1(node a,node b)BZOJ203815 { 16 if ( a.belong==b.belong ) return a.r<b.r; 17 return a.belong<b.belong; 18 } 19 20 bool cmp2(node a,node b) 21 { 22 return a.id<b.id; 23 } 24 25 ll gcd(ll x,ll y) 26 { 27 if ( y==0 ) return x; 28 return gcd(y,x%y); 29 } 30 31 void update(ll p,ll val) 32 { 33 ans-=num[a[p]]*num[a[p]];34 num[a[p]]+=val; 35 ans+=num[a[p]]*num[a[p]]; 36 } 37 38 int main() 39 { 40 ll n,m,i,j,k,x,y,z,l,r,sz; 41 while ( scanf("%lld%lld",&n,&m)!=EOF ) { 42 for ( i=1;i<=n;i++ ) scanf("%lld",&a[i]); 43 sz=sqrt(n); 44 for ( i=1;i<=m;i++ ) { 45 scanf("%lld%lld",&arr[i].l,&arr[i].r); 46 arr[i].id=i; 47 arr[i].belong=(arr[i].l-1)/sz+1; 48 } 49 sort(arr+1,arr+1+m,cmp1); 50 l=1; 51 r=0; 52 ans=0; 53 for ( i=1;i<=m;i++ ) { 54 for ( ;r<arr[i].r;r++ ) update(r+1,1); 55 for ( ;r>arr[i].r;r-- ) update(r,-1); 56 for ( ;l>arr[i].l;l-- ) update(l-1,1); 57 for ( ;l<arr[i].l;l++ ) update(l,-1); 58 if ( arr[i].l==arr[i].r ) { 59 arr[i].a=0; 60 arr[i].b=1; 61 continue; 62 } 63 arr[i].a=ans-(arr[i].r-arr[i].l+1); 64 arr[i].b=(ll)(arr[i].r-arr[i].l+1)*(arr[i].r-arr[i].l); 65 k=gcd(arr[i].a,arr[i].b); 66 arr[i].a/=k; 67 arr[i].b/=k; 68 } 69 sort(arr+1,arr+1+m,cmp2); 70 for ( i=1;i<=m;i++ ) printf("%lld/%lld\n",arr[i].a,arr[i].b); 71 72 } 73 return 0; 74 }
練習題:
1.(HDOJ4858)http://acm.hdu.edu.cn/showproblem.php?pid=4858
分析:圖的分塊。設點i的點權為val[i],與點i相鄰的項目的能量值之和為sum[i]。將圖中的點分為重點和輕點,重點是那些邊的度數超過sqrt(m)(該值可以自己規定)的點,除了重點剩下的點都是輕點。對於構圖,重點只和重點建邊,輕點可以和所有點建邊。每次更新,對於重點i和輕點來說來說都是更新自己的val[i]和相鄰點的sum[i]。而對於查詢操作來說,重點直接輸出sum[i],而輕點則采用暴力做法:遍歷其每一個相鄰點,答案累加上相鄰的val[i]。采用的思想是分攤復雜度的思想
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<cmath> 6 using namespace std; 7 typedef long long ll; 8 const ll maxn=1e5+100; 9 struct edge{ 10 ll u,v; 11 }arr[maxn]; 12 ll val[maxn],du[maxn],sum[maxn]; 13 bool ok[maxn]; 14 vector<ll>G[maxn]; 15 16 int main() 17 { 18 ll T,i,j,k,x,y,z,ans,cnt,n,m,sz,u,v,op,q; 19 scanf("%lld",&T); 20 while ( T-- ) { 21 scanf("%lld%lld",&n,&m); 22 for ( i=1;i<=n;i++ ) { 23 G[i].clear(); 24 ok[i]=false; 25 du[i]=val[i]=sum[i]=0; 26 } 27 for ( i=1;i<=m;i++ ) { 28 scanf("%lld%lld",&arr[i].u,&arr[i].v); 29 du[arr[i].u]++; 30 du[arr[i].v]++; 31 } 32 sz=sqrt(m); 33 for ( i=1;i<=n;i++ ) { 34 if ( du[i]>sz ) ok[i]=true; 35 } 36 for ( i=1;i<=m;i++ ) { 37 x=arr[i].u; 38 y=arr[i].v; 39 if ( ok[x] ) { 40 if ( ok[y] ) { 41 G[x].push_back(y); 42 G[y].push_back(x); 43 } 44 else { 45 G[y].push_back(x); 46 } 47 } 48 else { 49 if ( ok[y] ) { 50 G[x].push_back(y); 51 } 52 else { 53 G[x].push_back(y); 54 G[y].push_back(x); 55 } 56 } 57 } 58 scanf("%lld",&q); 59 while ( q-- ) { 60 scanf("%lld",&op); 61 if ( op==0 ) { 62 scanf("%lld%lld",&u,&x); 63 val[u]+=x; 64 for ( i=0;i<G[u].size();i++ ) { 65 v=G[u][i]; 66 sum[v]+=x; 67 } 68 } 69 else { 70 scanf("%lld",&u); 71 if ( ok[u] ) printf("%lld\n",sum[u]); 72 else { 73 ans=0; 74 for ( i=0;i<G[u].size();i++ ) { 75 v=G[u][i]; 76 ans+=val[v]; 77 } 78 printf("%lld\n",ans); 79 } 80 } 81 } 82 } 83 return 0; 84 }HDOJ4858
2.(HDOJ4467)http://acm.hdu.edu.cn/showproblem.php?pid=4467
題意:給你n個點(每個點都有一個顏色,0代表黑色,1代表白色),m條邊,每條邊有一個權值.現在有有兩個操作,一個是修改某個點的顏色(白變成黑/黑變成白),另外一個是詢問那些邊的兩個端點都為指定顏色的權值總和
分析:采用上題相同的思想。將所有點分為重點和輕點,但是這次重點和重點之前的邊要建在一個圖中,剩余的邊要建在另一個圖中。對於最後訪問的顏色,只有三種情況黑+黑(求和為0),黑+白(求和為1),白+白(求和為2),所以用a[0],a[1],a[2]分別對應的答案。對於重點i設置一個sum[i][2],sum[i][0]表示所有與他相鄰且顏色為0(黑)的點的邊權之和,sum[i][1]同理。更新時,對於重點i來說拿sum[i][0]和sum[i][1]去直接更新a數組,同時將其相鄰的重點的sum值進行修改。而對於輕點i來說,遍歷所有與i相連的邊,暴力更新a數組,而當其相鄰點為重點時則需要更新一下重點的sum數組。對於查詢操作,直接輸出a數組中的值即可
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<cmath> 6 using namespace std; 7 typedef long long ll; 8 const ll maxn=1e5+10; 9 ll sum[maxn][2],a[5],color[maxn],du[maxn]; 10 bool ok[maxn]; 11 struct Edge{ 12 ll x,y,val; 13 }arr[maxn],arr_[maxn]; 14 struct edge{ 15 ll v,val; 16 edge(ll _v=0,ll _val=0):v(_v),val(_val) {} 17 }; 18 vector<edge>G[maxn],G_[maxn]; 19 20 bool cmp(Edge x,Edge y) 21 { 22 if ( x.x==y.x ) return x.y<y.y; 23 return x.x<y.x; 24 } 25 26 int main() 27 { 28 ll n,m,i,j,k,x,y,z,sz,cnt,q,ans,h=0; 29 char op[10]; 30 while ( scanf("%lld%lld",&n,&m)!=EOF ) { 31 for ( i=1;i<=n;i++ ) { 32 sum[i][0]=sum[i][1]=0; 33 ok[i]=false; 34 du[i]=0; 35 G[i].clear(); 36 G_[i].clear(); 37 } 38 memset(a,0,sizeof(a)); 39 for ( i=1;i<=n;i++ ) scanf("%lld",&color[i]); 40 for ( i=1;i<=m;i++ ) { 41 scanf("%lld%lld%lld",&x,&y,&arr[i].val); 42 if ( x>y ) swap(x,y); 43 arr[i].x=x; 44 arr[i].y=y; 45 a[color[x]+color[y]]+=arr[i].val; 46 } 47 sort(arr+1,arr+1+m,cmp); 48 cnt=0; 49 for ( i=1;i<=m;i=j ) { 50 for ( j=i+1;j<=m;j++ ) { 51 if ( arr[i].x==arr[j].x && arr[i].y==arr[j].y ) { 52 arr[i].val+=arr[j].val; 53 } 54 else break; 55 } 56 arr_[++cnt]=arr[i]; 57 } 58 sz=sqrt(cnt); 59 for ( i=1;i<=cnt;i++ ) { 60 du[arr_[i].x]++; 61 du[arr_[i].y]++; 62 } 63 for ( i=1;i<=n;i++ ) { 64 if ( du[i]>sz ) ok[i]=true; 65 } 66 for ( i=1;i<=cnt;i++ ) { 67 x=arr_[i].x; 68 y=arr_[i].y; 69 if ( ok[x] ) { 70 if ( ok[y] ) { 71 G_[x].push_back(edge(y,arr_[i].val)); 72 G_[y].push_back(edge(x,arr_[i].val)); 73 sum[x][color[y]]+=arr_[i].val; 74 sum[y][color[x]]+=arr_[i].val; 75 } 76 else { 77 G[y].push_back(edge(x,arr_[i].val)); 78 sum[x][color[y]]+=arr_[i].val; 79 } 80 } 81 else { 82 if ( ok[y] ) { 83 G[x].push_back(edge(y,arr_[i].val)); 84 sum[y][color[x]]+=arr_[i].val; 85 } 86 else { 87 G[x].push_back(edge(y,arr_[i].val)); 88 G[y].push_back(edge(x,arr_[i].val)); 89 } 90 } 91 } 92 printf("Case %lld:\n",++h); 93 scanf("%lld",&q); 94 while ( q-- ) { 95 scanf("%s",op); 96 if ( op[0]==‘A‘ ) { 97 scanf("%lld%lld",&x,&y); 98 printf("%lld\n",a[x+y]); 99 } 100 else { 101 scanf("%lld",&x); 102 if ( ok[x] ) { 103 a[color[x]+0]-=sum[x][0]; 104 a[color[x]+1]-=sum[x][1]; 105 a[1-color[x]+0]+=sum[x][0]; 106 a[1-color[x]+1]+=sum[x][1]; 107 for ( i=0;i<G_[x].size();i++ ) { 108 y=G_[x][i].v; 109 z=G_[x][i].val; 110 sum[y][color[x]]-=z; 111 sum[y][1-color[x]]+=z; 112 } 113 } 114 else { 115 for ( i=0;i<G[x].size();i++ ) { 116 y=G[x][i].v; 117 z=G[x][i].val; 118 a[color[x]+color[y]]-=z; 119 a[1-color[x]+color[y]]+=z; 120 if ( ok[y] ) { 121 sum[y][color[x]]-=z; 122 sum[y][1-color[x]]+=z; 123 } 124 } 125 } 126 color[x]=1-color[x]; 127 } 128 } 129 } 130 return 0; 131 }HDOJ4467
專題訓練之莫隊算法