HDU 4455 Substrings 树状数组 + DP

题目大意:

就是现在给出一个长度不超过10^6的数组, 数组中的每个数1 <= a[i] <= 10^6, 现在定义一个子串(a_l, a_(l + 1), ... a_r) 的权值为这个子串中的不同的数的个数, 现在, 对于Q次询问, 每次询问1 <= w <= n, 输出给出的数组中长度为w的所有子串数组的权值和


大致思路:

思路都在代码注释里了..

第一次写树状数组...


代码如下:

Result  :  Accepted     Memory  :  29060 KB     Time  :  1840 ms

/*
 * Author: Gatevin
 * Created Time:  2015/3/10 10:36:27
 * File Name: Kotori_Itsuka.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

/*
 * 用dp[i]表示询问的w = i的时候对应的答案, 显然dp[1] = n
 * 那么对于给定的数组 a1, a2, .... an
 * dp[1]的组是 {a_1}, {a_2}, {a_3}, ... {a_(n - 1)}, {a_n}
 * dp[2]的组是 {a_1, a_2}, {a_2, a_3}, .... {a_(n - 1) a_n}
 * dp[i]的组是 {a_1, a_2, a_3, ... a_i}, {a_2, a_3, .. a_(i + 1)},.. {a_(n - i + 1), .. a_n}
 * 用tail[i]表示{a_(n - i + 1), ... a_n}的贡献值
 * 那么dp[i] = dp[i - 1] - tail[i] + ask(i);
 * 其中ask(i)表示dp[i]的分组中每一小组数加入新数之后的变化数
 * 对于数a_(i + 1)为例
 * 新加入的a_(i + 1)当它的上一次出现位置和这个位置的距离为 d 时
 * 在所有所有dp[k] (k <= d)在转移是都会因为这个数得到贡献
 * 所以需要用一个树状数组进行区间的更新, 然后再dp状态转移时进行单点查询即可
 */

#define maxn 1000010
int n, Q;
int a[maxn], w[10001], last[maxn], tail[maxn];//last[a[i]]为a[i]上一次出现的位置
lint dp[maxn];
lint C[maxn];

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

void add(int pos, int value)
{
    while(pos <= n)
        C[pos] += value, pos += lowbit(pos);
    return;
}

void update(int l, int r, int value)
{
    add(l, value), add(r + 1, -value);
}

lint ask(int pos)
{
    int ret = 0;
    while(pos)
        ret += C[pos], pos -= lowbit(pos);
    return ret;
}

int main()
{
    while(scanf("%d", &n), n)
    {
        for(int i = 1; i <= n; i++) scanf("%d", a + i);
        scanf("%d", &Q);
        for(int i = 1; i <= Q; i++) scanf("%d", w + i);
        memset(dp, 0, sizeof(dp));
        tail[1] = 1;
        dp[a[n]] = 1;
        for(int i = n - 1; i > 0; i--)
            if(!dp[a[i]])
                dp[a[i]] = 1, tail[n - i + 1] = tail[n - i] + 1;
            else tail[n - i + 1] = tail[n - i];
        memset(last, 0, sizeof(last));
        memset(C, 0, sizeof(C));
        for(int i = 1; i <= n; i++)
        {
            update(1, i - last[a[i]], 1);
            last[a[i]] = i;
        }
        memset(dp, 0, sizeof(dp));
        dp[1] = n;
        for(int i = 2; i <= n; i++)
            dp[i] = dp[i - 1] + ask(i) - tail[i - 1];
        for(int i = 1; i <= Q; i++)
            printf("%I64d\n", dp[w[i]]);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值