HDU 4455 Substrings(递推+优化)

来源:互联网 发布:农村淘宝 土豪村 编辑:程序博客网 时间:2024/05/22 14:31

该题是一道极好的递推+优化的题目(有很多人喜欢把递推归为DP,其实递推不具备DP的特点)。

因为对于每一个序列都要多次查询,每次查询长度为w的连续子序列中不同元素之和。  一开始确实没想到用递推,经验太少吧。  如果我们用d[i]表示长度为i的答案,那么由于子序列是连续的,所以d[i]和d[i-1]是有很大关系的。 首先很容易看出,d[i]的子序列比d[i-1]少一个,很容易发现,少了d[i-1]的最后一个序列,所以我们可以用O(n)的时间算出长度为i的最后一个序列中独一无二元素个数last[i] 。   然后从区间i-1长度增加到i增加了n-i+1个数增加的这些数为a[i],a[i+1],a[i+2]....a[n],则增加的这些数表示区间长度为i且以a[i,i+1,i+2....n]结尾,那么这些数中有多少是不能增加的呢?比如a[1]~a[i-1]存在a[i],以a[i]结尾的区间长度为i的区间就不用增加a[i]这个数。

接下来就比较巧妙了,我们可以处理出来所有元素距离前面一个相同元素的距离,那么距离大于区间长度的数才能加进来。由于不需要动态修改值,并不用树状数组,只需要一个数组就行。 挺卡内存的,只好重复利用了数组vis 。

细节参见代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long ll;const int maxn = 1000000+10;int T,n,m,x,q,last[maxn],len[maxn];ll a[maxn],vis[maxn];void init_last() {    memset(vis,0,sizeof(vis));    for(int i=n;i>=1;i--) {        if(!vis[a[i]]) {            vis[a[i]]++;            last[n-i+1] = last[n-i] + 1;        }        else last[n-i+1] = last[n-i];    }}void init_len() {    memset(vis,-1,sizeof(vis));    for(int i=1;i<=n;i++) {        if(vis[a[i]] == -1) {            vis[a[i]] = i;            len[i] = i+1;        }        else {            len[i] = i - vis[a[i]] + 1;            vis[a[i]] = i;        }    }}void init_bit() {    memset(vis,0,sizeof(vis));    for(int i=1;i<=n;i++) {        vis[len[i]]++;    }    for(int i=1;i<=n+1;i++) {        vis[i] += vis[i-1];    }}int main() {    while(~scanf("%d",&n)&&n) {        for(int i=1;i<=n;i++) scanf("%I64d",&a[i]);        init_last();        init_len();        init_bit();        a[1] = n;        for(int i=2;i<=n;i++) {            a[i] = a[i-1] - last[i-1] + vis[n+1] - vis[i];        }        scanf("%d",&q);        while(q--) {            scanf("%d",&x);            printf("%I64d\n",a[x]);        }    }    return 0;}


1 0