专题训练之莫队算法

推荐博客/专栏: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)
15 {
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 }

BZOJ2038

练习题:

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

原文地址:https://www.cnblogs.com/HDUjackyan/p/8996172.html

时间: 2024-08-30 02:42:12

专题训练之莫队算法的相关文章

「知识学习&amp;日常训练」莫队算法(一)(Codeforce Round #340 Div.2 E)

题意 已知一个长度为\(n\)的整数数列\(a[1],a[2],-,a[n]\),给定查询参数\(l,r\),问\([l,r]\)内,有多少连续子段满足异或和等于\(k\). 也就是说,对于所有的\(x,y (l\le x\le y\le r)\),能够满足\(a[x]\oplus a[x+1]\oplus ...\oplus a[y]=k\)的\((x,y)\)有多少组. 分析 对于这种离线区间的查询问题(不涉及对区间的更改),我们可以使用莫队算法解决.这类问题是什么类型?对于序列上的区间询问

莫队算法

Beautiful Girl 题意 给定一个长度为 n 的序列 a[1], a[2], ..., a[n] . m 组询问 (l, r, K) , 求区间 [l, r] 去除重复的数之后的第 K 小. n, m <= 100000 . 分析 莫队算法 + 值域分块. 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 #include &

BZOJ4241 历史研究 莫队算法 堆

欢迎访问~原文出处--博客园-zhouzhendong&AK 去博客园看该题解 题目 Description IOI国历史研究的第一人--JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. 日记中记录了连续N天发生的时间,大约每天发生一件. 事件有种类之分.第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大. JOI教授决定用如下的方法分析这些日记: 1.

CodeForces - 86D 莫队算法

http://codeforces.com/problemset/problem/86/D 莫队算法就是调整查询的顺序,然后暴力求解. 每回可以通过现有区间解ans(l,r)得到区间(l+1,r),(l-1,r),(l,r+1),(l,r-1)的区间解. 调整方式http://blog.csdn.net/bossup/article/details/39236275 这题比那个还要简单,查询的是K^2*Z,很清楚就是莫队算法,然而做的时候没有学过,回来补题补到 关键是我一直没明白为什么重载小于号

codeforces 617 E. XOR and Favorite Number(莫队算法)

题目链接:http://codeforces.com/problemset/problem/617/E 题目: 给你a1 a2 a3 ··· an 个数,m次询问:在[L, R] 里面又多少中 [l, r] 使得 al xor al+1 xor ··· ar 为 k. 题解: 本题只有区间查询没有区间修改,而且数据量不大(10w),所以可以用离线的方法解决. 使用莫队算法来解决,就需要O(1)的修改[L, R+1] .[L, R-1].[L+1, R].[L-1, R]. 详细的莫队可以百度学一

(普通的)莫队算法简单介绍

莫队算法(由莫涛发明的)是一种离线的暴力算法(至少我这么认为).使用莫队算法的条件是,知道一个区间[l, r]的结果,那么也可以快速知道[l + 1, r],[l - 1, r], [l, r - 1], [l, r + 1]这四个区间的结果.于是可以想到,直接通过这样转移来解决一些问题.当然有些出题人机智,故意卡这种暴力,让你从头跑到尾然后从尾跑到头,于是时间复杂度高达O(n2) 而莫队算法就是通过改变处理询问的顺序来降低时间复杂度. 比如说现在知道一个区间[l1, r1],又要转移到[l2,

莫队算法良心讲解

问题:有n个数组成一个序列,有m个形如询问L, R的询问,每次询问需要回答区间内至少出现2次的数有哪些. 朴素的解法需要读取O(nm)次数.如果使用STL的Map来保存出现的次数,每次需要O(nmlogn)的复杂度.有没有更快的方法呢? 注意到询问并没有强制在线,因此我们可以使用离线方法.注意到一点,如果我们有计算完[L, R]的map,那么[L - 1, R].[L + 1, R].[L, R - 1].[L, R + 1]都能够在O(logn)的复杂度得出.是否能安排适当的询问顺序,使得每次

清橙A1206 小Z的袜子(莫队算法)

A1206. 小Z的袜子 时间限制:1.0s   内存限制:512.0MB 总提交次数:744   AC次数:210   平均分:44.44 将本题分享到: 查看未格式化的试题   提交   试题讨论 试题来源 2010中国国家集训队命题答辩 问题描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是

HYSBZ 2038 小Z的袜子(hose) (莫队算法)

题意:中文题. 析:很著名的莫队算法,先把这个求概率的式子表达出来,应该是分子:C(x1, 2) + C(x2, 2) + C(x3, 2) + ... + C(xn, 2)  分母:C(n, 2),然后化成分数的表达形式,[x1(x1-1)+x2(x2-1)+...+xn(xn-1)] / (n*(n-1))  然后再化简得到 (sigma(xi*xi)  - n) / (n*(n-1)) ,然后就是对每个区间进行运算,离线,把所以的序列分成sqrt(n)块,然后用两个指针,进行对数据的计算.