zoukankan      html  css  js  c++  java
  • BZOJ5443:[CEOI2018]Lottery

    我对状态空间的理解:https://www.cnblogs.com/AKMer/p/9622590.html

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=5443

    这题能在暴力美学分组中占压轴地位,是不简单的。我从未见过如此灵性的暴力题目(也许是我太弱了题目写少了)。

    我们令(ans[j][i])表示与串([i,i+l-1])距离为(j)的串的个数,最暴力的方法显然是直接(O(n^2*l))去匹配的。

    但是其中我们会对一些字符进行若干次重复的比较。比如([1,3])([2,4])匹配时以及([2,4])([3,5])都会判断(a[3])是否等于(a[3])。很显然这种冗余的操作是不优的。那么我们怎么减少这样的操作呢?

    性质:如果我们已经知道了([l1,r1])([l2,r2])的距离,那么我们可以在(O(1))时间内算出([l1+1,r1+1])([l2+1,r2+1])的距离。设前者为(s),后者为(t),那么(t=s-(a[l1]!=a[l2])+(a[r1+1]!=a[r2+1]))

    也就是说,我们可以在(O(n))的时间内匹配完(n)条子串。

    我们枚举一个(len),令(h1)(1)(h2)(h1+len),然后暴力匹配([h1,h1+l-1])([h2,h2+l-1]),再在(O(n))的时间内把所以起点下标差为(len)的所以串对匹配完并且统计答案。

    枚举(len)的复杂度乘以匹配的复杂度为(O(n^2)),我们成功消去了一个(l)

    但是这题还卡空间……所以(ans)数组开不了那么大。

    可是出题人关上了一扇门,却为我们留了一扇窗。

    (query)的次数很小。我们可以将([1,l])之内的所有数字都找到离自己最近的大于等于自己的(k),用一个(pos[i])记录这个(k)。然后每次匹配出来距离为(dis),我们就把答案累计到(ans[pos[dis]][i])里去,最后前缀和一下,就可以求出所有的(query)了。

    时间复杂度:(O(n^2))

    空间复杂度:(O(nq))

    代码如下:

    #include <cstdio>
    #include <algorithm>
    using namespace std;
      
    const int maxn=1e4+5;
      
    bool v[maxn];
    int n,m,l,cnt;
    int ans[101][maxn];
    int a[maxn],k[101],pos[maxn];
      
    int read() {
        int x=0,f=1;char ch=getchar();
        for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
        for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
        return x*f;
    }
      
    void solve() {
        for(int len=1;len<=n-l;len++) {//枚举len
            int res=0,h1=1,h2=h1+len;//res记录当前两个串的距离,h1是第一个串的起点,h2是第二个串的起点
            for(int i=0;i<l;i++)
                if(a[h1+i]!=a[h2+i])res++;
            ans[pos[res]][h1]++;
            ans[pos[res]][h2]++;//答案累计到ans[pos[res]][h1]和ans[pos[res]][h2]里
            while(1) {
                if(h2+l>n)break;//如果h2往后没有l那么长了就break
                res-=(a[h1]!=a[h2]);
                res+=(a[h1+l]!=a[h2+l]);//O(1)转移
                h1++;h2++;
                ans[pos[res]][h1]++;
                ans[pos[res]][h2]++;//累计答案
            }
        }
    }
      
    int main() {
        n=read();l=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        m=read();pos[1]=1;//pos[1]等于1
        for(int i=1;i<=m;i++)
            k[i]=read(),v[k[i]]=1;//我这里用差分的方法求每个数的pos
        for(int i=2;i<=l;i++)
            pos[i]=pos[i-1]+v[i-1];//如果是在(k[i],k[i+1]]之间的数,pos[i]就是i+1
        solve();
        for(int i=1;i<=n-l+1;i++)
            for(int j=1;j<=m;j++)
                ans[j][i]+=ans[j-1][i];//前缀和统计答案
        for(int i=1;i<=m;i++) {
            for(int j=1;j<=n-l+1;j++)
                printf("%d ",ans[pos[k[i]]][j]);//离线输出答案
            puts("");
        }
        return 0;
    }
    
  • 相关阅读:
    多媒体基础知识之PCM数据
    FFmpeg在Linux下编译使用
    AndroidStudio 中使用FFMPEG
    Android 音频播放分析笔记
    【Linux 命令】- more和less
    【Linux】- 简明Vim练习攻略
    【Linux】- 对find,xargs,grep和管道的一些理解
    【Linux 命令】- find 命令
    【Linux 命令】- tar 命令
    【Linux】- CentOS7 下 安装 supervisor
  • 原文地址:https://www.cnblogs.com/AKMer/p/9641894.html
Copyright © 2011-2022 走看看