题目
今天复习树状数组,敲完模板写到了这道题。对着题想了老半天就是想不出来这题和树状数组有什么关系,就算是存这一位的数值,那么也没法向后转移。。。。
沉思后,尝试着离线对线段排序做暴力,T到妈都不认识。。。
接着娴熟的打开题解,研究一番。发现除了离线排序之外,还有奥秘。破解这道题的关键在于用树状数组统计每个点对前面的贡献。想到,这应该也是这道题用树状数组解的一种思考角度吧。考虑元素对定向区间的贡献这个角度还真是挺常见的,所以写篇博客总结记录下。
具体思路:
加入树状数组的定向区间的贡献表示,数组中该元素对其前面区间的新元素贡献数。说白些,前面有这个元素,该位置就是1否则就是0.
处理的时候先将所有区间读入,按照右端点排序做离线处理。接着维护一个元素数组的下标r,遍历区间时把r到新区间内的元素加入树状数组维护。加入树状数组时,考虑该元素是否在前面出现过,若出现过,则删去前面位置的贡献,改为该点的贡献。
这样一来,从第一个元素到当前区间端点这一区间内可以保证所有相同元素的贡献都是由最右边的那个产生的。于是,维护完之后,只需要的到当前区间 l 到 r 的区间贡献和即可。
混合c和c++风格的AC代码;
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1000050;
int a[N];
int num[N];
int pos[N];
int ans[N];
int n,m;
struct node
{
int l;
int r;
int code;
}b[N];
int lowBit(int k)
{
return k & (-k);
}
int cmp(node a,node b)
{
return a.r < b.r;
}
void add(int k,int num)
{
while(k <= n)
{
a[k] += num;
k += lowBit(k);
}
}
int get(int k)
{
int sum = 0;
while(k)
{
sum += a[k];
k -= lowBit(k);
}
return sum;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d",&num[i]);
}
scanf("%d",&m);
for(int i = 0;i < m;i++)
{
scanf("%d%d",&b[i].l,&b[i].r);
b[i].code = i;
}
sort(b,b + m,cmp);
int r = 0;
node v;
for(int i = 0;i < m;i++)
{
v = b[i];
while(r < v.r)
{
r++;
if(pos[num[r]])
{
add(pos[num[r]],-1);
}
pos[num[r]] = r;
add(pos[num[r]],1);
}
ans[v.code] = get(v.r) - get(v.l - 1);
}
for(int i = 0;i < m;i++)
{
printf("%d\n",ans[i]);
}
}
正确但T到怀疑人生的java代码:
import java.util.Arrays;
import java.util.Scanner;
class s implements Comparable<s>
{
int l;
int r;
int code;
public s(int l, int r, int code)
{
this.l = l;
this.r = r;
this.code = code;
}
public int compareTo(s o)
{
return r - o.r;
}
}
class TreeArray {
private int[] array;
private int size;
public TreeArray(int n)
{
size = n;
array = new int[n + 1];
}
private int lowBit(int k)
{
return k & (-k);
}
public void add(int k,int num)
{
while(k <= size)
{
array[k] += num;
k += lowBit(k);
}
}
public int get(int k)
{
int sum = 0;
while(k > 0)
{
sum += array[k];
k -= lowBit(k);
}
return sum;
}
public int get(int l,int r)
{
return get(r) - get(l - 1);
}
}
public class Main {
static int[] num = new int[1000050];
static int[] ans = new int[1000050];
static int[] c = new int[1000050];
static s[] a;
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
for(int i = 1;i <= n;i++)
{
num[i] = scan.nextInt();
}
int m = scan.nextInt();
a = new s[m];
for(int i = 0;i < m;i++)
{
a[i] = new s(scan.nextInt(),scan.nextInt(),i);
}
Arrays.sort(a);
s v;
TreeArray ta = new TreeArray(n);
int r = 0;
for(int i = 0;i < m;i++)
{
v = a[i];
while(r < v.r)
{
r++;
if(c[num[r]] != 0)
{
ta.add(c[num[r]], -1);
}
ta.add(r, 1);
c[num[r]] = r;
}
ans[v.code] = ta.get(v.l,v.r);
}
for(int i = 0;i < m;i++)
{
System.out.println(ans[i]);
}
}
}