题目:
链接:https://www.nowcoder.com/acm/contest/139/J
来源:牛客网
Given a sequence of integers a1, a2, ..., an and q pairs of integers (l1, 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 a1, a2, ..., ai, aj, aj + 1, ..., an.
题意 :给 a1,a2,a3,,,an 询问q次 (i,j) 每次问 a1, a2, ..., ai, aj, aj + 1, ..., an.有多少种数。
解法一:离线的树状数组。
可以将数组复制一份在后面求R-L+n.
/**
先将询问按照r排序
然后依次将数放进数组
如果这个数出现过 将之前位置清零,当前位置+1
如果这个是没有出现过
就在当前位置+1
求 L-R 中有多少种数,
这里把 1-L 中把一种数最后出现的位置作为贡献值,这样 Sum(R) - Sum(L-1) 就有正确性。
注意当我们更新到R时, Sum(L)不是 1-L中的数的个数(只能表示在1-R中只出现在1-L中的数有多少种),
Sum(R)才是表示 1-R中有多少种数。
*/
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long int ll;
const int maxn = 100000;
struct Query{
int l,r,id;
}Q[maxn+5];
int n,q,a[maxn*2+5],C[maxn*2+5],ans[maxn+5],last[maxn+5];
int lowbit(int x)
{
return x&-x;
}
int Sum(int x)
{
int sum = 0;
while(x>0) sum+=C[x],x-=lowbit(x);
return sum;
}
void Add(int x,int d)
{
while(x<=n*2) C[x]+=d,x+=lowbit(x);
}
bool cmp(Query n1,Query n2)
{
return n1.r < n2.r;
}
int main()
{
while(~scanf("%d %d",&n,&q))
{
for(int i=1;i<=n;i++) scanf("%d",&a[i]), a[n+i] = a[i];
for(int i=1;i<=q;i++)
{
int l,r;
scanf("%d %d",&l,&r);
Q[i].l = r, Q[i].r = n+l, Q[i].id = i;
}
sort(Q+1,Q+1+q,cmp);
for(int i=1;i<=n*2;i++) C[i] = 0;
for(int i=1;i<=n;i++) last[i] = 0;
int cur = 1;
for(int i=1;i<=q;i++)
{
while(cur<=Q[i].r&&cur<=n*2)
{
if(last[a[cur]]) Add(last[a[cur]],-1);//把以前的贡献去掉
last[a[cur]] = cur, Add(last[a[cur]],1);//最新的贡献
cur++;
}
ans[Q[i].id] = Sum(Q[i].r) - Sum(Q[i].l-1);
}
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
}
return 0;
}
解法二:主席树。
主席树求L-R中有多少个数和树状数组一样的思想只是主席树可以在线因为主席树相当存了n个树状数组嘛。
不过当 复制数组用主席树会超时。
我们可以先求出只出现在 L-R中有多少种 x (求出每种数的最左出现和最右出现 当一个数 li>=L&&ri<=R说明它只出现在 L-R中),用种数num 减去 x ,ans=num-x。
给出 <SPOJ DQUERY - D-query> 的大佬题解:https://blog.csdn.net/VictorZC8/article/details/52780846
/**
这里的主席树和 《SPOJ DQUERY - D-query》中 主席树(1-n代表得不一样)有区别,
这里的主席树就是经典的求kth。我也是做了这题才知道
主席树是可以如此灵活运用的,菜啊!
**/
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 100000;
typedef long long int ll;
int n,a[maxn+5],b[maxn+5],th[maxn+5],l[maxn+5],r[maxn+5];
int tot,sz,root[maxn*2+5],ls[maxn*50+5],rs[maxn*50+5],sum[maxn*50+5];
void build(int &v,int l,int r)
{
v=++tot;
sum[v] = 0;
if(l==r) return;
int mid = (l+r)>>1;
build(ls[v],l,mid);
build(rs[v],mid+1,r);
}
void updata(int &v,int last,int l,int r,int q)
{
v = ++tot;
ls[v] = ls[last];
rs[v] = rs[last];
sum[v] = sum[last]+1;
if(l==r) return;
int mid = (l+r)>>1;
if(q<=mid) updata(ls[v],ls[last],l,mid,q);
if(q>mid) updata(rs[v],rs[last],mid+1,r,q);
}
int query(int v,int last,int l,int r,int L,int R)
{
if(r<L||l>R) return 0;
int mid = (l+r)>>1;
if(L<=l&&r<=R) return sum[v] - sum[last];
int cost = 0;
if(mid>=L) cost+=query(ls[v],ls[last],l,mid,L,R);
if(mid<R) cost+=query(rs[v],rs[last],mid+1,r,L,R);
return cost;
}
int q,pos[maxn+5],num;
struct Node
{
int l,r;
} P[maxn+5];
bool cmp(Node n1, Node n2)
{
return n1.l < n2.l;
}
int main()
{
while(~scanf("%d %d",&n,&q))
{
num = tot = 0;
for(int i=0; i<=n; i++) pos[i] = 0;
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
}
for(int i=1; i<=n; i++) r[i]=l[i]=0;
for(int i=1; i<=n; i++)
{
r[a[i]] = i;
if(l[a[i]]==0) l[a[i]] = i;
}
for(int i=1; i<=n; i++)
{
if(l[i])
{
num++;
P[num].l = l[i], P[num].r = r[i];
}
}
sort(P+1,P+num+1,cmp);
build(root[0],1,n);
for(int i=1; i<=num; i++) updata(root[i],root[i-1],1,n,P[i].r);
P[num+1].l = n+1;//加一个边界,以防二分有可能找不到值 昨天就是没有写这里挂了。
root[num+1] = root[num];
while(q--)
{
int L,R;
scanf("%d %d",&L,&R);
if(R<=L||L+1==R) printf("%d\n",num);
else
{
L++,R--;
int li=0,ri=num+1,mid;
while(ri-li>1)
{
mid = (li+ri)/2;
if(P[mid].l>=L) ri = mid;
else li = mid;
}
printf("%d\n",num-query(root[num+1],root[ri-1],1,n,L,R));
}
}
}
return 0;
}
/*
6 3
1 2 1 2 3 4
2 4
1 6
3 5
5 3
1 1 1 1 1
1 1
1 5
2 4
*/