HH的项链
这道题主要有两个数据版本
也主要有两个做法
一个是莫队
时
间
复
杂
度
O
(
n
n
)
时间复杂度O(n \sqrt n)
时间复杂度O(nn)
另一个是树状数组
时
间
复
杂
度
O
(
n
l
o
g
n
)
时间复杂度O(n log n)
时间复杂度O(nlogn)
洛谷题目链接
牛客题目链接
牛客的这道题数据会水一点,莫队可以过
题目描述:
输入样例
6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出样例
2
2
4
第一种主要做法
莫队做法
就是个裸的莫队了,没什么好讲的,直接贴代码了
精彩的主要是第二种做法
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+7;
int ans[maxn];
int n;
int block_length;
struct query_node{
int l,r;
int id;
}q[maxn];
int a[maxn],cnt[maxn];
inline int read(){
int n=0;
char c=getchar();
bool f=0;
while(c!='-'&&(c<'0'||c>'9'))c=getchar();
if(c=='-'){f=1;c=getchar();}
while(c>='0'&&c<='9'){n=n*10+c-'0';c=getchar();}
if(f)return -n;
return n;
}
char write_[50];
inline void write(int n){
if(n==0){putchar('0');return;}
if(n<0){putchar('-');n=-n;}
int t=0;
while(n){write_[t++]=n%10+'0';n/=10;}
while(t--)putchar(write_[t]);
}
inline void add(int x){
if(cnt[a[x]]==0)++ans[0];
++cnt[a[x]];
}
inline void del(int x){
--cnt[a[x]];
if(cnt[a[x]]==0)--ans[0];
}
inline bool cmp(query_node a,query_node b){
return (a.l/block_length)^(b.l/block_length)?a.l<b.l:(((a.l/block_length)&1)?a.r<b.r:a.r>b.r);
}
int main(){
n=read();
for(int i=1;i<=n;++i)a[i]=read();
int T;
T=read();
for(int i=1;i<=T;++i){
q[i].l=read();q[i].r=read();
q[i].id=i;
}
block_length=sqrt(T);
sort(q+1,q+T+1,cmp);
int L=0,R=0;
for(int i=1;i<=T;++i){
while(L<q[i].l)del(L++);
while(R>q[i].r)del(R--);
while(L>q[i].l)add(--L);
while(R<q[i].r)add(++R);
ans[q[i].id]=ans[0];
}
for(int i=1;i<=T;++i)write(ans[i]),printf("\n");;
return 0;
}
第二种做法 —— 树状数组
树状数组做法
这题还可以用树状数组写,感觉有点莫队的思想。
首先我们可以知道对于区间内的重复的数,我们可以只计算最右边的数。
像这样
那么对于固定的右区间,我们可以通过前缀和来处理出不同左区间带来的答案
那么我们就可以通过枚举右端点,然后用树状数组O(log)前缀和求出所需要求的区间答案
那么接下来就只剩下处理出各个点权值了
我们记录每个数上一次出现的地方
每次添加一个数的时候,当前位置++,上一次出现的地方–
问题就解决了
很像莫队的思想
AC代码:
#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
using namespace std;
const double eps = 1e-8;
const int maxn = 2e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1<<30;
inline void swap(int &x, int &y){x^=y^=x^=y;}
inline int gcd(int a,int b) {return !b ? a : gcd(b,a%b);}
int n, m, k;
int sum[maxn], ans[maxn];
int a[maxn], c[maxn], pre[maxn];
struct q_node{
int l, r;
int id;
}q[maxn];
bool cmp(q_node a, q_node b){
return a.r < b.r;
}
int lowbit(int x){return x & (-x);}
void _add(int x, int w){
while(x <= n){
c[x] += w;
x += lowbit(x);
}
}
int _sum(int x){
int sum = 0;
while(x > 0){
sum += c[x];
x -= lowbit(x);
}
return sum;
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
scanf("%d", &m);
for(int i = 1; i <= m; ++i)
scanf("%d %d", &q[i].l, &q[i].r), q[i].id = i;
sort(q + 1, q + m + 1, cmp);//给区间排序,保证所求区间右端点递增
int r = 1;//固定左区间
for(int i = 1; i <= m; ++i){
while(r <= q[i].r){//处理到右区间
_add(r, 1);
if(pre[a[r]])_add(pre[a[r]], -1);
pre[a[r]] = r;
r++;
}
ans[q[i].id] = _sum(q[i].r) - _sum(q[i].l - 1);//计算答案
}
for(int i = 1; i <= m; ++i){
printf("%d\n", ans[i]);
}
return 0;
}
采花
洛谷题目链接
牛客题目链接
思路与上题相同,处理上稍有变化而已
输入样例
5 3 5
1 2 2 3 1
1 5
1 2
2 2
2 3
3 5
输出样例
2
0
0
1
0
AC代码:
#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
using namespace std;
const double eps = 1e-8;
const int maxn = 2e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1<<30;
inline void swap(int &x, int &y){x^=y^=x^=y;}
inline int gcd(int a,int b) {return !b ? a : gcd(b,a%b);}
int n, m, k;
int ans[maxn];
int a[maxn], c[maxn], pre[maxn], ppre[maxn];
struct q_node{
int l, r;
int id;
}q[maxn];
bool cmp(q_node a, q_node b){
return a.r < b.r;
}
int lowbit(int x){return x & (-x);}
void _add(int x, int w){
while(x <= n){
c[x] += w;
x += lowbit(x);
}
}
int _sum(int x){
int sum = 0;
while(x > 0){
sum += c[x];
x -= lowbit(x);
}
return sum;
}
int main(){
scanf("%d %d %d", &n, &k, &m);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for(int i = 1; i <= n; ++i){
}
for(int i = 1; i <= m; ++i)
scanf("%d %d", &q[i].l, &q[i].r), q[i].id = i;
sort(q + 1, q + m + 1, cmp);
int r = 1;
for(int i = 1; i <= m; ++i){
while(r <= q[i].r){
if(pre[a[r]]){
_add(pre[a[r]], 1);
if(ppre[a[r]]) _add(ppre[a[r]], -1);
ppre[a[r]] = pre[a[r]];
}
pre[a[r]] = r;
r++;
}
ans[q[i].id] = _sum(q[i].r) - _sum(q[i].l - 1);
}
for(int i = 1; i <= m; ++i){
printf("%d\n", ans[i]);
}
return 0;
}