莫队算法HDU校赛 by蒋神
2014-03-20 20:13
344 查看
题解来自hdu-蒋神,转载请注明出处
Total Submission(s) : 68 Accepted Submission(s) : 23
接下来共T组,首先输入n,sum分别表示共n个数以及给定的和,再下一行依次输入n个数ai,然后输入一个q,表示共有q个询问,接下来q行每行输入l,r,表示要求的是区间[l,r].
范围:
T<=20
n <= 20000,sum <= 20000
1 <= ai < sum
q <= 20000,1<= l < r <= n
题解:
现在知道[l,r]的答案ans,可以O(1)得出[l,r+1]的答案
c[i]表示现在值为i的数有几个,那么a[r+1]=x对答案贡献了c[M-x],所以[l,r+1]的答案就是ans+c[M-x]
同理可以O(1)得到[l-1,r]的答案
void insert(int x){
x=a[x];
ans+=c[M-x];
++c[x];
}
现在知道[l,r]的答案ans,可以O(1)得出[l,r-1]的答案
假设a[r]=x,要分两种情况讨论:
1)x+x!=M
, 答案是 ans-c[M-x]
2)x+x==M,答案是
ans-(c[M-x]-1)
同理可以O(1)得到[l+1,r]的答案
void erase(int x){
x=a[x];
ans-=c[M-x]-(x+x==M);
--c[x];
}
现在知道[p,q]的答案,现在求[l,r]的答案
考虑右端点:
1)如果q<r,把a[q+1],a[q+2]…a[r]插入
2)如果q>r,把a[r+1],a[r+2]…a[q]删除
时间复杂度是|q-r|
考虑左端点:
1)如果p<l,把a[p],a[p+1]…a[l]删除
2)如果p>l,把a[l+1],a[l+2]…a[p]插入
时间复杂度是|p-l|
这个过程时间复杂度就是|p-l|+|q-r|
while(q<r) insert(++q);
while(q>r) erase(q--);
while(p<l) erase(p++);
while(p>l) insert(--p);
1)读入所有的询问,并记下是第几个询问
2)把N个数分成sqrt(N)块,算出每个询问的左端点在哪个块
pos=l/sqrt(N)
3)把询问以pos为第一关键字,r为第二关键字排序
4)按新的顺序求出所有询问的答案
struct Query{
int id,l,r,pos;
bool operator<(const Query &t)const{
returnpos!=t.pos?pos<t.pos:r<t.r;
}
};
Query b[21000];
sort(b+1,b+Q+1); //
把b[1],b[2]...b[Q]排序
这个算法时间复杂度是O(N^1.5)
为什么呢?
1)对于那些左端点属于同一个块的询问
i)因为r是递增的,最差情况q从块的最左端加到N,O(N),一共N^0.5个块,所以这部分的复杂度是O(N^1.5)的
ii)p一次询问最多变化N^0.5,所以这部分的时间复杂度是O(Q*N^0.5)
2)从一个块跨越到另外一个块时
i)p最多变化2*N^0.5,最多跨越N^0.5,这部分复杂度是O(N)
ii)最差情况下,q从N移动到最左端
N和Q范围一样,所以整个算法复杂度是O(N^1.5)
如果时间复杂度允许,可以用这个思想做很多区间询问的题目,但有个前提,整个过程中,不能有修改操作
完整代码
#define DeBUG
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <string>
#include <set>
#include <sstream>
#include <map>
#include <bitset>
using namespace std ;
#define zero {0}
#define INF 2000000000
#define EPS 1e-6
typedef long long LL;
const double PI = acos(-1.0);
//#pragma comment(linker, "/STACK:102400000,102400000")
inline int sgn(double x)
{
return fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1);
}
int a[20005];
int c[20005];
long long t[20005];
int ans;
int M,N,Q;
void insert(int x)
{
x = a[x];
ans += c[M - x];
++c[x];
}
void erase(int x)
{
x = a[x];
ans -= c[M - x] - (x + x == M);
--c[x];
}
struct Query
{
int id, l, r, pos;
bool operator<(const Query &t)const
{
return pos != t.pos ? pos < t.pos : r < t.r;
}
};
Query b[21000];
int main()
{
// freopen("C:\\Users\\Sky\\Desktop\\1.in","r",stdin);
int _;
scanf("%d", &_);
while (_--)
{
scanf("%d%d", &N, &M);
int i;
for (i = 1; i <= N; i++)
scanf("%d", &a[i]);
scanf("%d", &Q);
for (i = 1; i <= Q; i++)
{
b[i].id = i;
scanf("%d%d", &b[i].l, &b[i].r);
b[i].pos = b[i].l / sqrt(N);
}
sort(b + 1, b + Q + 1);
memset(c, 0, sizeof(c));
ans = 0;
int p = 1, q = 1;
insert(1);
for (i = 1; i <= Q; i++)
{
int l = b[i].l, r = b[i].r;
while (q < r) insert(++q);
while (q > r) erase(q--);
while (p < l) erase(p++);
while (p > l) insert(--p);
t[b[i].id] = ans;
}
for (i = 1; i <= Q; i++) printf("%I64d\n", t[i]);
}
}
分块?
Time Limit : 2000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other)Total Submission(s) : 68 Accepted Submission(s) : 23
Font: Times New Roman | Verdana | Georgia
Font Size: ← →
Problem Description
给定n个数,记为ai(1<=i <= n),求区间内某两个数和为给定的sum的对数。Input
第一行输入整数T,表示共有T组.接下来共T组,首先输入n,sum分别表示共n个数以及给定的和,再下一行依次输入n个数ai,然后输入一个q,表示共有q个询问,接下来q行每行输入l,r,表示要求的是区间[l,r].
范围:
T<=20
n <= 20000,sum <= 20000
1 <= ai < sum
q <= 20000,1<= l < r <= n
Output
输出q行,回答每个询问的值.Sample Input
1 4 4 1 2 2 3 3 1 2 2 3 1 4
Sample Output
0 1 2
Source
test题解:
现在知道[l,r]的答案ans,可以O(1)得出[l,r+1]的答案
c[i]表示现在值为i的数有几个,那么a[r+1]=x对答案贡献了c[M-x],所以[l,r+1]的答案就是ans+c[M-x]
同理可以O(1)得到[l-1,r]的答案
void insert(int x){
x=a[x];
ans+=c[M-x];
++c[x];
}
现在知道[l,r]的答案ans,可以O(1)得出[l,r-1]的答案
假设a[r]=x,要分两种情况讨论:
1)x+x!=M
, 答案是 ans-c[M-x]
2)x+x==M,答案是
ans-(c[M-x]-1)
同理可以O(1)得到[l+1,r]的答案
void erase(int x){
x=a[x];
ans-=c[M-x]-(x+x==M);
--c[x];
}
现在知道[p,q]的答案,现在求[l,r]的答案
考虑右端点:
1)如果q<r,把a[q+1],a[q+2]…a[r]插入
2)如果q>r,把a[r+1],a[r+2]…a[q]删除
时间复杂度是|q-r|
考虑左端点:
1)如果p<l,把a[p],a[p+1]…a[l]删除
2)如果p>l,把a[l+1],a[l+2]…a[p]插入
时间复杂度是|p-l|
这个过程时间复杂度就是|p-l|+|q-r|
while(q<r) insert(++q);
while(q>r) erase(q--);
while(p<l) erase(p++);
while(p>l) insert(--p);
1)读入所有的询问,并记下是第几个询问
2)把N个数分成sqrt(N)块,算出每个询问的左端点在哪个块
pos=l/sqrt(N)
3)把询问以pos为第一关键字,r为第二关键字排序
4)按新的顺序求出所有询问的答案
struct Query{
int id,l,r,pos;
bool operator<(const Query &t)const{
returnpos!=t.pos?pos<t.pos:r<t.r;
}
};
Query b[21000];
sort(b+1,b+Q+1); //
把b[1],b[2]...b[Q]排序
这个算法时间复杂度是O(N^1.5)
为什么呢?
1)对于那些左端点属于同一个块的询问
i)因为r是递增的,最差情况q从块的最左端加到N,O(N),一共N^0.5个块,所以这部分的复杂度是O(N^1.5)的
ii)p一次询问最多变化N^0.5,所以这部分的时间复杂度是O(Q*N^0.5)
2)从一个块跨越到另外一个块时
i)p最多变化2*N^0.5,最多跨越N^0.5,这部分复杂度是O(N)
ii)最差情况下,q从N移动到最左端
N和Q范围一样,所以整个算法复杂度是O(N^1.5)
如果时间复杂度允许,可以用这个思想做很多区间询问的题目,但有个前提,整个过程中,不能有修改操作
完整代码
#define DeBUG
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <string>
#include <set>
#include <sstream>
#include <map>
#include <bitset>
using namespace std ;
#define zero {0}
#define INF 2000000000
#define EPS 1e-6
typedef long long LL;
const double PI = acos(-1.0);
//#pragma comment(linker, "/STACK:102400000,102400000")
inline int sgn(double x)
{
return fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1);
}
int a[20005];
int c[20005];
long long t[20005];
int ans;
int M,N,Q;
void insert(int x)
{
x = a[x];
ans += c[M - x];
++c[x];
}
void erase(int x)
{
x = a[x];
ans -= c[M - x] - (x + x == M);
--c[x];
}
struct Query
{
int id, l, r, pos;
bool operator<(const Query &t)const
{
return pos != t.pos ? pos < t.pos : r < t.r;
}
};
Query b[21000];
int main()
{
// freopen("C:\\Users\\Sky\\Desktop\\1.in","r",stdin);
int _;
scanf("%d", &_);
while (_--)
{
scanf("%d%d", &N, &M);
int i;
for (i = 1; i <= N; i++)
scanf("%d", &a[i]);
scanf("%d", &Q);
for (i = 1; i <= Q; i++)
{
b[i].id = i;
scanf("%d%d", &b[i].l, &b[i].r);
b[i].pos = b[i].l / sqrt(N);
}
sort(b + 1, b + Q + 1);
memset(c, 0, sizeof(c));
ans = 0;
int p = 1, q = 1;
insert(1);
for (i = 1; i <= Q; i++)
{
int l = b[i].l, r = b[i].r;
while (q < r) insert(++q);
while (q > r) erase(q--);
while (p < l) erase(p++);
while (p > l) insert(--p);
t[b[i].id] = ans;
}
for (i = 1; i <= Q; i++) printf("%I64d\n", t[i]);
}
}
相关文章推荐
- hdu 5381 2015多校第八场 莫队算法
- Lucky - HDU 5213 莫队算法
- hdu 3333 Turing Tree(线段树/数状数组)或莫队算法
- HDU 5381 The sum of gcd (技巧,莫队算法)
- hdu 5213 容斥&& 莫队算法
- hdu 5213 Lucky && 莫队算法的理解
- HDU 4638 Group 莫队算法
- HDU 4638 Group (莫队算法)
- hdu 5145 莫队算法模板题
- HDU 5145 (莫队算法)
- HDU - 5213 Lucky(莫队算法+容斥思想)
- HDU 4638 Group 【树状数组,分块乱搞(莫队算法?)】
- [反演 莫队算法] HDU 4676 Sum Of Gcd
- HDU 5381 The sum of gcd 询问区间内所有子区间的GCD和 [莫队算法]
- hdu_4467_Graph(莫队算法思想)
- hdu 5381 莫队算法/gcd
- hdu 5381 The sum of gcd 原来有个算法叫莫队 2015 Multi-University Training Contest 8
- hdu 5145 NPY and girls && 莫队算法 && 逆元处理
- hdu_4467_Graph(莫队算法思想)
- HDU 4358 Boring counting 莫队算法