这个性质我本来是想用线段树写的,但是忽略了每次只是访问该点往前的区间,所以没能写出来

本质上还是一个动态维护前缀和的问题

tr[i], 表示在当前位置上有不同的点 每次我们求当前位置到某一位置的不同的点的数量,只需要保证往前看的序列中重复的点只保留最后一个即可

例题:2019山东省赛 : BaoBao Loves Reading (金牌题)

思路:每次添加一个点,只需要考虑一下这个点对整个答案序列的贡献即可

算法:树状数组 + 差分

#include  <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int a[N];
int dif[N];
int pre[N];
int tr[N], n;

int lowbit(int x) {
    return x & -x;
}

int add(int x, int v) {
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += v; 
}

int sum(int x) {
    int res = 0;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

void init() {
    for(int i = 0; i <= n; i ++ ) {
        dif[i] = 0;
        tr[i] = 0;
        pre[i] = 0;
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    
    while(T -- ) {
        
        
        scanf("%d", &n);
        
        init();
        
        for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
        
        for(int i = 1; i <= n; i ++ ) {
            int j = a[i];
            if(pre[j] == 0) {
                dif[0] ++;
                add(i, 1);
            }
            else {
                add(i, 1);
                add(pre[j], -1);
                int d = sum(i) - sum(pre[j]);
                dif[0] ++, dif[d] --;
                //printf("i : %d d : %d\n", i, d);
            }
            pre[j] = i;
        }
        
        //cout << tr[1].f << '\n';
        
        for(int i = 1; i <= n; i ++ ) {
            dif[i] += dif[i -1];
            printf("%d", dif[i]);
            if(i != n) printf(" ");
        }
        puts("");
    }
    
    return 0;
}