hdu 4455(dp+树状数组)

题意:给出n个数字的序列,然后q次询问,长度为w的子串的不同数字个数之和。
题解:f[i]表示长度为w的子串不同数字个数之和,状态转移方程f[i] = f[i - 1] - 最后i-1个不同数字个数 + 每组从i-1到i多加的数字是否与前i-1个数字重复,那么最后i-1个不同数字个数循环一遍就能得到,然后用树状数组维护每个数字左边长度为i的区间内出现和自己相同的数字的个数之和。思路出自
http://blog.csdn.net/xingyeyongheng/article/details/25833549

#include 
#include 
#include 
#define ll long long
using namespace std;
const int N = 1000005;
int n, q, m, a[N], pos[N];
ll C[N], f[N], last[N];

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

void modify(int x, int d) {
    while (x <= n) {
        C[x] += d;
        x += lowbit(x);
    }
}

ll query(int x) {
    ll ret = 0;
    while (x > 0) {
        ret += C[x];
        x -= lowbit(x);
    }
    return ret;
}

int main() {
    while (scanf("%d", &n) == 1 && n) {
        memset(C, 0, sizeof(C));
        memset(pos, -1, sizeof(pos));
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            if (pos[a[i]] != -1) {
                modify(i - pos[a[i]] + 1, 1);
                modify(i + 1, -1);
            }
            pos[a[i]] = i;
        }
        memset(pos, -1, sizeof(pos));
        pos[a[n]] = n;
        last[1] = 1;
        for (int i = 2, j = n - 1; i <= n; i++, j--) {
            last[i] = last[i - 1];
            if (pos[a[j]] == -1)
                last[i]++;
            pos[a[j]] = j;
        }
        f[1] = n;
        for (int i = 2; i <= n; i++)
            f[i] = f[i - 1] - last[i - 1] + n - i + 1 - query(i);
        scanf("%d", &q);
        while (q--) {
            scanf("%d", &m);
            printf("%lld\n", f[m]);
        }
    }
    return 0;
}

你可能感兴趣的:(ACM-DP,ACM-树状数组)