hdu 4455 Substrings

比赛的时候一直往离线+数据结构上想 sigh,不过最后正解中也的确带有 "离线处理"

---------------------------------------------------------------------------------------------------------------------------------

首先我们可以很明显地发现 区间个数是$nq$级别的 所以莫队什么的还不如直接暴力

然后 我们还可以估计得出最后的答案是可以爆$int$的 所以直接去对每个位置算贡献也是不可行的

但是算贡献的这种思路并没有错 因为我们多想想就会发现 把一些边界情况处理了后

每个位置的贡献实际上就是一个连续的区间 (从长度为$L1$的字串到长度为$L2$的子串)

然后这里区间的话 由于每个长度的我们都需要求出来 (递推地求) 所以并不需要什么数据结构 直接前缀和就好了

---------------------------------------------------------------------------------------------------------------------------------

我使用的递推公式为

$f[i]=f[i-1]-cnt[i-1]+(n-i+1)-sum[i]$

$f[i]$即为长度为$i$的子串的答案

$cnt[i]$为最后一个子串的贡献(长度+1后 最后一个子串由于右端点无法向右扩展 就不合法了)

$(n-i+1)$就是之前的长度+1还合法的子串在理想情况下的贡献

$sum[i]$则是由于某些数可能在一个子串中出现多次 所以贡献要减掉

求$sum$可以根据每个数左边最近的一个相同的数的位置预处理出来

(从某处开始有+1的贡献 到某处结束时再有-1的贡献 其实也就是对整个区间有贡献)

---------------------------------------------------------------------------------------------------------------------------------

由于多处压了空间($f$数组以及$cnt$数组) 代码可能并不方便看

#include <bits/stdc++.h>
using namespace std;
const int N=1000010;
struct que
{
    int num,ask;
    long long ans;
}ques[10010];
bool cmp1(const que &aa,const que &bb)
{
    return aa.ask<bb.ask;
}
bool cmp2(const que &aa,const que &bb)
{
    return aa.num<bb.num;
}
int num[N],la[N],sum[N];
bool used[N];
int n,q;
int main()
{
    int cnt,lacnt;
    while(scanf("%d",&n),n)
    {
        for(int i=1;i<=n;++i)
            sum[i]=0;
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&num[i]);
            if(la[num[i]])
            {
                ++sum[i-la[num[i]]+1];
                --sum[i+1];
            }
            la[num[i]]=i;
        }
        cnt=0;
        scanf("%d",&q);
        for(int i=1;i<=q;++i)
        {
            ques[i].num=i;
            scanf("%d",&ques[i].ask);
            ques[i].ans=0;
        }
        sort(ques+1,ques+1+q,cmp1);
        long long f,laf=0;
        int itail=1;
        while(ques[itail].ask==0&&itail<=q)
            ++itail;
        for(int i=1;i<=n;++i)
        {
            lacnt=cnt;
            if(!used[num[n-i+1]])
            {
                used[num[n-i+1]]=true;
                cnt=lacnt+1;
            }
            else
                cnt=lacnt;
            sum[i+1]+=sum[i];
            f=laf-lacnt+(n-i+1)-sum[i];
            while(itail<=q&&ques[itail].ask==i)
            {
                ques[itail].ans=f;
                ++itail;
            }
            if(itail>q)
            break;
            laf=f;
        }
        sort(ques+1,ques+1+q,cmp2);
        for(int i=1;i<=q;++i)
            printf("%lld\n",ques[i].ans);
        for(int i=1;i<=n;++i)
        {
            la[num[i]]=0;
            used[num[i]]=false;
        }
    }
    return 0;
}

还有一个java版本的 不过一开始MLE 压了空间后还TLE

import java.io.*;
import java.math.*;
import java.util.*;
import java.text.*;
public class Main
{
    final static int N=1000010;
    static class que
    {
        int num,ask;
        long ans;
    }
    static class cmp1 implements Comparator <que>
    {
        public int compare(que aa,que bb)
        {
            return aa.ask-bb.ask;
        }
    }
    static class cmp2 implements Comparator <que>
    {
        public int compare(que aa,que bb)
        {
            return aa.num-bb.num;
        }
    }
    public static void main(String args[])
    {
        Scanner cin=new Scanner(new BufferedInputStream(System.in));
        int num[]=new int[N];
           int la[]=new int[N];
        int sum[]=new int[N];
        boolean used[]=new boolean[N];
        que[] ques=new que[10010];
        for(int i=1;i<=10000;++i)
            ques[i]=new que();
        int n,q,cnt,lacnt;
        n=cin.nextInt();
        while(n!=0)
        {
            for(int i=1;i<=n;++i)
                sum[i]=0;
            for(int i=1;i<=n;++i)
            {
                num[i]=cin.nextInt();
                if(la[num[i]]!=0)
                {
                    ++sum[i-la[num[i]]+1];
                    --sum[i+1];
                }
                la[num[i]]=i;
            }
            cnt=0;
            q=cin.nextInt();
            for(int i=1;i<=q;++i)
            {
                ques[i].num=i;
                ques[i].ask=cin.nextInt();
                ques[i].ans=0;
            }
            Arrays.sort(ques,1,q,new cmp1());
            long f,laf=0;
            int itail=1;
            while(ques[itail].ask==0&&itail<=q)
                ++itail;
            for(int i=1;i<=n;++i)
            {
                lacnt=cnt;
                if(!used[num[n-i+1]])
                {
                    used[num[n-i+1]]=true;
                    cnt=lacnt+1;
                }
                else
                    cnt=lacnt;
                sum[i+1]+=sum[i];
                f=laf-lacnt+(n-i+1)-sum[i];
                while(itail<=q&&ques[itail].ask==i)
                {
                    ques[itail].ans=f;
                    ++itail;
                }
                if(itail>q)
                    break;
                laf=f;
            }
            Arrays.sort(ques,1,q,new cmp2());
            for(int i=1;i<=q;++i)
                System.out.println(ques[i].ans);
            for(int i=1;i<=n;++i)
            {
                la[num[i]]=0;
                used[num[i]]=false;
            }
            n=cin.nextInt();
        }
    }
}
时间: 2025-01-05 23:26:52

hdu 4455 Substrings的相关文章

hdu 4455 Substrings(树状数组+递推)

题目链接:hdu 4455 Substrings 题目大意:给定一个长度为N的序列,现在有Q次询问,每次给定一个w,表示长度,输出序列中长度为w的连续子序列 的权值和.序列的权值表示序列中不同元素的个数. 解题思路:递推,先预处理处每个位置和前面相同的数据的最短距离P.dp[i]表示说长度为i子序列的权值和,dp[i+1] = dp[i] + v - c.v为[i+1~N]中P值大于i的个数,我们可以看作将长度为i的子序列长度向后增加1,那么v则为增加长度带来 的权值增加值,c则是最后一个长度为

HDU 4455 Substrings(预处理+dp)

题目大意:给你n个数字,然后m次查询,每次给你一个x,让你求出来1到x,2到x+1...不同数的和. 需要各种预处理,处理出来所有的间隔之间有多少相同的数字,处理出来最后一个被去掉的间隔有多少个不重复的数字. dp[i] = dp[i-1]-S+T.S代表最后被略去的那个区间的不同的数,T代表新区间扩张之后每个区间增加的不同的数的和. Substrings Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K

hdu 4455 Substrings (DP 预处理思路)

Substrings Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1727    Accepted Submission(s): 518 Problem Description XXX has an array of length n. XXX wants to know that, for a given w, what is

HDU 4455 Substrings(DP)

题目链接:点击打开链接 思路: 我们用d[i]表示长度为i的答案.   那么我们可以把样例数据写出来看看是否能够递推.   可以发现, d[i] = d[i-1] - last[i-1] + (n-i+1) - dist[i]. last[i]表示从后往前i长度时不同数字的个数, 也就是说, d[i-1]的最后一个子序列被扔了, 然后d[i]比d[i-1]还可能多加了( n - i + 1), 然后我们把不符合要求的减掉.  比赛时自己推出来的, 说不很清楚, 建议自己推一下. 细节参见代码:

HDU 4455 Substrings --递推+树状数组优化

题意: 给一串数字,给q个查询,每次查询长度为w的所有子串中不同的数字个数之和为多少. 解法:先预处理出D[i]为: 每个值的左边和它相等的值的位置和它的位置的距离,如果左边没有与他相同的,设为n+8(看做无穷). 考虑已知w=k的答案,推w = k+1时,这时每个区间都将增加一个数,即后n-k个数会增加到这些区间中,容易知道,如果区间内有该数,那么个数不会加1,,即D[i] > k时,结果++,即查询后n-k个数有多少个D[i] > k 的数即为要加上的数,然后最后面还会损失一个区间,损失的

HDU 4455 Substrings (2012年杭州赛区现场赛C题)

1.题目描述:点击打开链接 2.解题思路:本题利用dp解决.不过这个dp的思路的确比太容易想到.需要观察规律才能发现.我们可以从贡献值的角度考虑.以题目中给的样例来说明这种方法. 通过观察相邻两个w值,我们会发现一个事实:每个大区间都包含了小区间的解(这里的解即原题中的sum值).但是这还不够,观察图上标记为红色的数字,它们可以看做是上一个w值对应的区间多出来的新数字,如果我们可以算出这些红色数字的贡献值,那么我们就能得到当前w下的解.那么该如何计算呢? 继续观察后会发现,每次w增加1,区间个数

HDU 4455 Substrings(线性dp,很有意思)

题意:这个题给你n个数,然后有q组询问,无修改,每次询问一个长度x,问你所有长度为x的区间价值加和是多少,区间的价值的计算是这样定义的,一个区间的价值就等于这个区间中不同数字的个数 思路:看了题解虽然理解了,但感觉还是很难想啊,原本这个题卡在了怎么把n2的降维成n的,这个题目也算是很经典的一个套路吧,疯狂预处理,把所需要的信息不断转化,疯狂降维预处理,然后达到On递推,感觉对于dp[i]和dp[i-1]之间的关系,单点贡献的那一部分不怎么容易想到,而且想到了单点贡献,也想不到怎么预处理,QAQ

poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题

题目:http://poj.org/problem?id=1226 http://acm.hdu.edu.cn/showproblem.php?pid=1238 其实用hash+lcp可能也可以,甚至可能写起来更快,不过我没试,我最近在练习后缀数组,所以来练手 后缀数组的典型用法之一----------------后缀数组+lcp+二分 思路:1.首先将所有的字符串每读取一个,就将其反转,作为一组,假设其下标为i到j,那么cnt[i]到cnt[j]都标记为一个数字(这个数字意思是第几个读入的字符

Substrings(hdu 4455)

题意: 给定一个序列ai,个数为n.再给出一系列w:对于每个w,求序列中,所有长度为w的连续子串中的权值和,子串权值为子串中不同数的个数. /* dp[i]表示长度为i的序列不同元素个数之和. 考虑从i-1向i转移的时候,最后i-1个元素的贡献会消失,记last[i]表示最后i个元素中不重复元素的个数,则转移时,它的贡献是-last[i]. 同时每个序列会多出a[i],a[i+1],a[i+2]等元素,这些元素对答案有贡献的前提是与前i-1个元素不重复. 那么i这个元素对答案有贡献的范围是pos