10. Given a sequence of integers a1, a2, ..., an and q pairs of integers (l 1, r1), (l2, r2), ..., (lq, rq), find
count(l1, r1),
count(l2, r2), ..., count(lq, rq) where count(i, j) is the number of different integers among a 1, a2, ..., ai, aj,
aj + 1,
..., an.
输入描述:
The input consists of several test cases and is terminated by end-of-file.
The first line of each test cases contains two integers n and q.
The second line contains n integers a 1, a2, ..., an.
The i-th of the following q lines contains two integers l i and ri.
输出描述:
For each test case, print q integers which denote the result.
备注
1 ≤ n, q ≤ 1e5
1 ≤ ai ≤ n
1 ≤ li, ri ≤ n
The number of test cases does not exceed 10.
示例1:
输入
3 2
1 2 1
1 2
1 3
4 1
1 2 3 4
1 3
输出
2 1 3
题目大意:给n个数,q次查询,接下来q行表示q个操作,l和r,操作为从1到l,r到n区间内找有几个不同的数
想法:题意就读错,是输入l,r,在1到l,r到n找不同的数,我想成了从l 到n区间内,对于此题没什么想法,数据1e5,暴力二重循环要超时,其他数据结构也不知道,后来通过学长的讲解,知道可以用树状数组,复杂度nlogn,现在可以套板子,原理还没搞懂,毕竟看学长的代码就很难看懂了。。。自己写更不可能了,以下代码来自学长。。。
思路:
首先考虑将两个区间并成一个区间,将原数组扩展为2倍长的,即 a[i+n] = a[i],首尾相连。对于查询a[1...l]和a[r..n]有多少种不同的数字可以转换为查询 a[r...l+n]有多少种不同的数字。
然后开两个数组,firs和next,first[i]记为1表示这个位置的数第一次出现,以下再出现记为0,之后树状数组查询更新用的first数组。next数组记录这一个数下一次出现的位置。
离线查询,对依次输入的区间按左端点进行排序,用sort(属于c++的stl内容,这两天刚搞懂,需要再系统了解下stl,毕竟很方便,常常因为不会c++而与你们格格不入),这样区间就是一直右移的,就可以只刷新要用的右半部分树状数组。
最难的是数组更新与维护,下标从0开始遍历,如果等于输入的需要查询的左端点,就可以统计1的个数了,用树状数组查询得结果sum(y)-sum(x-1)。否则fir[Next[i]]=1,更新下一个为1。
注:
树状数组模板
更新,第x个数更新为y。
void update(int x,int y)
{
while(x<=n)
{
c[x]+=y;
x+=lowbit(x);
}
}
树状数组维护的是数组的前缀和,比如sum(x)就是a[x]的前缀和,想查询l~r区间的元素和只需要求出来sum(r)-sum(l-1),这里的sum函数十分的神奇:
int sum(int x)
{
int ans=0;
while(x>0)
{
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
其中学长的三行代码,,我理解了一天,,(灬ꈍ ꈍ灬),
数组循环完是这样子的。。。
a[i] | 1 | 2 | 1 | 1 | 2 | 1 |
first[i] | 1 | 1 | 0 | 0 | 0 | 0 |
next[i] | 3 | 5 | 4 | 6 | 0 | 0 |
//预处理Next、Fir数组
for (int i = 2 * n; i > 0; i--) {
fir[loc[a[i]]] = 0;
Next[i] = loc[a[i]];
loc[a[i]] = i;
}
//Name:牛客多校第一场 J-Different Integers 树状数组
//============================================================================
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 100005;
const int MAXN = 2 * maxn;
struct ss {
int x, y, pos;
} f[maxn];
//loc[i] = i出现的位置
int loc[maxn], ans[maxn];
//Next[i] = i位置上的数下一个出现的位置
//fir[i] = i位置上的数是否为第一次出现
int a[MAXN], c[MAXN], Next[MAXN], fir[MAXN];
bool cmp(ss p, ss q) {
return p.x < q.x;
}
int lowbit(int x) {
return x & -x;
}
//a[i] += v
void add(int x, int v) {
while (x < MAXN) {
c[x] += v;
x += lowbit(x);
}
}
//a[1] + ... + a[x]
int sum(int x) {
int s = 0;
while (x) {
s += c[x];
x -= lowbit(x);
}
return s;
}
//void display(int n) {
// for(int i = 1; i <= 2 * n; i++) {
// printf("i = %d a[i] = %d next[i] = %d fir[i] = %d sum = %d\n", i, a[i], next[i], fir[i], sum(i));
// }
//}
int main() {
int n, q;
while (~scanf("%d %d", &n, &q)) {
//初始化变量,Fir初始化为1, 其余初始化为0
memset(a, 0, sizeof(a));
memset(loc, 0, sizeof(loc));
memset(Next, 0, sizeof(Next));
memset(ans, 0, sizeof(ans));
memset(c, 0, sizeof(c));
// 倍增数组,将对两个区间的查询转换为对单个区间的查询
for (int i = 1; i <= n; i++) {
// cin>>a[i];
scanf("%d", &a[i]);
a[i + n] = a[i];
fir[i] = 1;
fir[i + n] = 1;
}
//预处理Next、Fir数组
for (int i = 2 * n; i > 0; i--) {
fir[loc[a[i]]] = 0;
Next[i] = loc[a[i]];
loc[a[i]] = i;
}
//预处理树状数组
for (int i = 1; i <= 2 * n; i++) {
if (fir[i]) {
add(i, 1);
}
}
// for(int i = 1; i <= 2 * n; i++) {
// printf("i = %d a[i] = %d next[i] = %d fir[i] = %d sum = %d\n", i, a[i], next[i], fir[i], sum(i));
// }
//区间处理,
for (int i = 1; i <= q; i++) {
scanf("%d %d", &f[i].x, &f[i].y);
// cin>>f[i].x>>f[i].y;
f[i].x += n;
swap(f[i].x, f[i].y);
f[i].pos = i;
}
// 区间排序
sort(f + 1, f + q + 1, cmp);
// for (int i =1; i <= q; i++) {
// printf("f[i].x = %d f[i].y = %d f[i].pos = %d\n", f[i].x, f[i].y, f[i].pos);
// }
int nextLIndex = 1;
for (int i = 1; i <= 2 * n && nextLIndex <= q;) {
if (i == f[nextLIndex].x) {
// Query
ans[f[nextLIndex].pos] = sum(f[nextLIndex].y) - sum(f[nextLIndex].x-1);
nextLIndex++;
// printf("f[i].pos = %d ans = %d\n", f[i].pos, ans[f[i].pos]);
} else {
if (fir[i]) {
// Update,下一个更新为 1
fir[i] = 0;
add(i, -1);
fir[Next[i]] = 1;
add(Next[i], 1);
// display(n);
}
i++;
}
}
for (int i = 1; i <= q; i++) {
// cout<<ans[i]<<endl;
printf("%d\n", ans[i]);
}
}
return 0;
}