码迷,mamicode.com
首页 > 其他好文 > 详细

数列区间询问中的分块思想

时间:2015-11-10 00:02:37      阅读:301      评论:0      收藏:0      [点我收藏+]

标签:

分块算法主要用于给定序列的区间询问问题,能够以较小的时间代价暴力求解,时间复杂度一般在O(n*n^0.5)。关键在O(1) 维护好某一区间在增加或者减少一个边界元素所带来的影响。需要注意的就是在更新的区间的时候要先放大在缩小,否则可能出现当前区间左右边界互换的情况,这 个影响某一些题可能没有影响,但是极有可能出错。

时间复杂度:先考虑左边界的时间复杂度,由于分成了sqrt(n)块,而同一块中左标移动的范围最多是sqrt(n),那相邻块跳 转的情况呢?可以虚拟出每块中有至少一个询问进行思考,那么相邻块之间的移动次数最大为2*sqrt(n)。由于共有Q次询问,因此最终时间复杂度为 O(Q*sqrt(n))。再考虑右边界,对于同一块内的右边界来说,其值是单调递增的,因此时间复杂度为O(n),相邻块跳转为O(2*n),由于共有 sqrt(n)块,因此最终时间复杂度为O(n*sqrt(n))。

---以上参考自 http://www.cnblogs.com/Lyush/archive/2013/08/16/3263247.html

 

1. Codefoces 86D Powerful array

 

题意: 给定n个数的一个序列, 再给出t组查询 (n, t <= 200000)问区间[l, r]里每个数的出现次数的平方乘上这个数的和是多少。

分析:分块的模板题,将询问离线处理,令sum[x]表示x在区间内出现的次数。扫描到ai时,若是进入区间则ans+=(2*sum[ai]+1)*ai, sum[ai]++,若是出区间则sum[ai]--,ans-=(2*sum[ai]+1)*ai。暴力维护区间即可。

 

技术分享
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<vector>
 8 #include<set>
 9 #include<string>
10 typedef long long LL;
11 using namespace std;
12 #define pii pair<int,int>
13 const int inf = 0x3f3f3f3f;
14 const int N = 200010;
15 
16 struct Node{
17     int id;
18     int L, R, bel;
19     Node(int id=0, int L=0, int R=0, int bel=0) : id(id),L(L),R(R),bel(bel){}
20     bool operator < (const Node &cmp) const {
21         if(bel != cmp.bel) return bel < cmp.bel;
22         return R < cmp.R;
23     }
24 } node[N];
25 LL a[N];
26 LL ans[N];
27 int cnt[1000100];
28 int L, R;
29 LL ret;
30 
31 LL query(int l, int r)
32 {
33     for(int i=l; i<L; i++) {
34         ret += (cnt[a[i]]<<1|1)*a[i];
35         cnt[a[i]]++;
36     }
37     for(int i=R+1; i<=r; i++) {
38         ret += (cnt[a[i]]<<1|1)*a[i];
39         cnt[a[i]]++;
40     }
41     for(int i=L; i<l; i++) {
42         cnt[a[i]]--;
43         ret -= (cnt[a[i]]<<1|1)*a[i];
44     }
45     
46     for(int i=r+1; i<=R; i++) {
47         cnt[a[i]]--;
48         ret -= (cnt[a[i]]<<1|1)*a[i];
49     }
50     L = l, R = r;
51     return ret;
52 }
53 
54 int main()
55 {
56     int i,j,k,m,n, x, y;
57     while(scanf("%d %d",&n,&m) == 2)
58     {
59         int block = sqrt(n*1.0);
60         for(i=1; i<=n; i++) scanf("%I64d",a+i);
61         for(i=1; i<=m; i++) {
62             scanf("%d %d",&x,&y);
63             node[i] = Node(i, x, y, x/block);
64         }
65         sort(node+1, node+1+m);
66         memset(cnt, 0, sizeof(cnt));
67         L = R = ret = 0;
68         for(i=1; i<=m; i++) {
69             ans[node[i].id] = query(node[i].L, node[i].R);
70         }
71         for(i=1; i<=m; i++) printf("%I64d\n", ans[i]);
72     }
73     return 0;
74 }
View Code

 

2. HDU 4638 Group

 

题意:给定一个序列,序列由1-N个元素全排列而成,求任意区间连续的段数。例如序列2,3,5,6,9就是三段(2, 3) (5, 6)(9)。

分析:也是离线处理,分块排好序,O(1)的维护就是每新添加一个元素 t 查看 t-1 与 t+1 是否都在区间内,如是则段数-1,如果都不在,则段数+1。

 

技术分享
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<vector>
 8 #include<set>
 9 #include<string>
10 typedef long long LL;
11 using namespace std;
12 #define pii pair<int,int>
13 const int inf = 0x3f3f3f3f;
14 const int N = 100010;
15 
16 struct Node{
17     int id;
18     int L, R, bel;
19     Node(int id=0, int L=0, int R=0, int bel=0) : id(id),L(L),R(R),bel(bel){}
20     bool operator < (const Node &cmp) const {
21         if(bel != cmp.bel) return bel < cmp.bel;
22         return R < cmp.R;
23     }
24 } node[N];
25 int a[N];
26 int ans[N];
27 int vis[N];
28 int L, R;
29 int ret;
30 
31 int query(int l, int r, int k)
32 {
33     if(k == 1) {
34         ret = 0;
35         for(int i=l; i<=r; i++) {
36             if(vis[a[i]-1] && vis[a[i]+1])
37                 ret--;
38             else if(!vis[a[i]-1] && !vis[a[i]+1])
39                 ret++;
40             vis[a[i]] = 1;
41         }
42         L = l, R = r;
43         return ret;
44     }
45     for(int i=l; i<L; i++) {
46         if(vis[a[i]-1] && vis[a[i]+1])
47             ret--;
48         else if(!vis[a[i]-1] && !vis[a[i]+1])
49             ret++;
50         vis[a[i]] = 1;
51     }
52     for(int i=R+1; i<=r; i++) {
53         if(vis[a[i]-1] && vis[a[i]+1])
54             ret--;
55         else if(!vis[a[i]-1] && !vis[a[i]+1])
56             ret++;
57         vis[a[i]] = 1;
58     }
59     for(int i=L; i<l; i++) {
60         if(vis[a[i]-1] && vis[a[i]+1])
61             ret++;
62         else if(!vis[a[i]-1] && !vis[a[i]+1])
63             ret--;
64         vis[a[i]] = 0;
65     }
66     for(int i=r+1; i<=R; i++) {
67         if(vis[a[i]-1] && vis[a[i]+1])
68             ret++;
69         else if(!vis[a[i]-1] && !vis[a[i]+1])
70             ret--;
71         vis[a[i]] = 0;
72     }
73     L = l, R = r;
74     return ret;
75 }
76 
77 int main()
78 {
79     int i,j,k,m,n, x, y;
80     int t;
81     scanf("%d",&t);
82     while(t--)
83     {
84         scanf("%d%d",&n,&m);
85         int block = sqrt(n*1.0);
86         for(i=1; i<=n; i++) scanf("%d",a+i);
87         for(i=1; i<=m; i++) {
88             scanf("%d %d",&x,&y);
89             node[i] = Node(i, x, y, x/block);
90         }
91         sort(node+1, node+1+m);
92         memset(vis, 0, sizeof(vis));
93         for(i=1; i<=m; i++) {
94             ans[node[i].id] = query(node[i].L, node[i].R, i);
95         }
96         for(i=1; i<=m; i++) printf("%d\n", ans[i]);
97     }
98     return 0;
99 }
View Code

 

数列区间询问中的分块思想

标签:

原文地址:http://www.cnblogs.com/WCB-ACM/p/4951372.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!