• 7/26 思维+dp+后缀数组的学习


    C. The Third Problem

    题意:给定一个数组a,要求数组a和数组b相似,相似要求:对于任意一个区间,两数组的最小非负整数值相同,问有多少满足要求的数组b
    思路:
    1.记录数组a中0~n-1值所在的下标位置;
    2.从0到n-1枚举i,并且记录0到i-1出现的数的最左和最右端点[L,R]
    3.若a[i]出现在数i-1的最左和最右端点中,则r-l+1(表示数的个数,即区间长度)-i(固定位置值的个数)

    #include 
    #define int long long
    #define endl '\n'
    
    using namespace std;
    const int N=1e5+5;
    const int mod=1e9+7;
    int n,a[N];
    
    signed main()
    {
        int t;cin>>t;
        while(t--)
        {
            cin>>n;
            for(int i=1;i<=n;i++)
            {
                int x;cin>>x;
                a[x]=i;
            }
            int l=N,r=-1,ans=1;
            for(int i=0;i<n;i++)
            {
                if(a[i]>=l&&a[i]<=r)
                    ans*=(r-l+1-i),ans%=mod;
                l=min(a[i],l); //记录a[i]的左右端点值
                r=max(a[i],r);
                //cout<
            }
            cout<<ans<<endl;
        }
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    D. Increase Sequence

    题意:将数组a中的值都转化为h,且左右区间只能出现一次。
    思路:
    1.若数组相邻数的差值大于1,则不存在方法满足题意得方法;a[i]=h-x
    2.设置状态:dp[i]表示把a[1]~a[i]都变为0的最小操作次数
    3.状态转移:
    d=0,可用操作数为a[i]+1(1表示在i-1出结束,在i出开始)
    d=-1,可用操作数为a[i-1],在i-1处结束
    d==1,需要新开区间,dp[i]=dp[i-1]

    #include 
    #define int long long
    #define endl '\n'
    
    using namespace std;
    const int N=1e5+5;
    const int mod=1e9+7;
    int n,h,a[N],dp[2005];  //dp[i]表示把a[1]~a[i]都变为0的最小操作次数
    
    signed main()
    {
        cin>>n>>h;
        for(int i=1;i<=n;i++)
        {
            int x;cin>>x;
            a[i]=h-x;
        }
        dp[0]=1;
        int flag=0;
        n++;
        for(int i=1;i<=n;i++)
        {
            int res=a[i]-a[i-1];
            if(abs(res)>=2)
                flag=1;
            if(res==0)
                dp[i]=dp[i-1]*(a[i]+1)%mod;
            if(res==-1)
                dp[i]=dp[i-1]*a[i-1]%mod;
            if(res==1)
                dp[i]=dp[i-1];
        }
        if(flag)
            cout<<0<<endl;
        else
            cout<<dp[n]<<endl;
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    后缀数组

    模板:(注解在代码中)

    const int N=1e5+5;
    const int mod=1e9+7;
    int n,k;
    int rk[N];   //以i开头后缀的排名
    char s[N]; 
    int sa[N];   //表示sa[i]表示排名i的后缀的开头下标
    
    //求解各个以i为起始下标的后缀字符串的排名
    bool cmp(int i,int j)
    {
        if(rk[i]!=rk[j])
            return rk[i]<rk[j];
        int ri=(i+k<=n ? rk[i+k]:-1);
        int rj=(i+k<=n ? rk[j+k]:-1);
        return ri<rj
    }
    void getsa(int n,char *str)
    {
        int rk2[N];
        for(int i=1;i<=n;i++)
            sa[i]=i,rk[i]=s[i];  //利用ASCLL码
        for(k=1;k<=n;k*=2)
        {
            sort(sa+1,sa+1+n;cmp);
            rk2[sa[1]]=1;
            for(int i=2;i<=n;i++)
                rk2[sa[i]]=rk2[[sa[i-1]]]+cmp(sa[i-1],sa[i]);
            for(int i=1;i<=n;i++)
                rk[i]=rk2[i];
        }
    }
    
    int ht[N]   //维护数组rk相邻两个后缀的lcp(i-1和i的最长公共前缀)
    void getht(int n,char *s)
    {
        for(int i=1;i<=n;i++)
            rk[sa[i]]=1;
        int h=0;
        ht[1]=0;
        for(int i=1;i<=n;i++)
        {
            int j=sa[rk[i]-1];
            if(h>0)
                h--;
            for(;j+h<=n&&i+h<=n;h++)
                if(s[j+n]!=s[i+h])
                    break;
            ht[rk[i]]=h;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    P4248 [AHOI2013]差异

    本题所用知识:后缀数组+单调栈
    i和j的最长公共前缀:i~j区间内ht[i]的最小值
    套路:每个区间的区间最小值之和,使用单调栈解决。
    ll[i]:表示往左看,小于等于h[i]的下标位置
    rr[i]:表示从i往右看,小于等于h[i]的下标位置
    算以h[i]为最小值的区间个数为:(i-ll[i])*(rr[i]-i)
    可能数据更新了,这种做法会re,原因在与于cmp的排序规则增加了复杂度。

    #include 
    #define int long long
    #define endl '\n'
    
    using namespace std;
    const int N=5e5+5;
    const int mod=1e9+7;
    int n,k;
    int rk[N],rk2[N];   //以i开头后缀的排名
    char s[N];
    int sa[N];   //表示sa[i]表示排名i的后缀的开头下标
    
    //求解各个以i为起始下标的后缀字符串的排名
    bool cmp(int i,int j)
    {
        if(rk[i]!=rk[j])
            return rk[i]<rk[j];
        int ri=(i+k<=n ? rk[i+k]:-1);
        int rj=(j+k<=n ? rk[j+k]:-1);
        return ri<rj;
    }
    void getsa(int n,char *str)
    {
        for(int i=1;i<=n;i++)
            sa[i]=i,rk[i]=s[i];  //利用ASCLL码
        for(k=1;k<=n;k*=2)
        {
            sort(sa+1,sa+1+n,cmp);
            rk2[sa[1]]=1;
            for(int i=2;i<=n;i++)
                rk2[sa[i]]=rk2[sa[i-1]]+cmp(sa[i-1],sa[i]);
            for(int i=1;i<=n;i++)
                rk[i]=rk2[i];
        }
    }
    
    int ht[N];   //维护数组rk相邻两个后缀的lcp(i-1和i的最长公共前缀)
    void getht(int n,char *s)
    {
        for(int i=1;i<=n;i++)
            rk[sa[i]]=i;
        int h=0;
        ht[1]=0;
        for(int i=1;i<=n;i++)
        {
            int j=sa[rk[i]-1];
            if(h>0)
                h--;
            for(;j+h<=n&&i+h<=n;h++)
                if(s[j+h]!=s[i+h])
                    break;
            ht[rk[i]]=h;
        }
    }
    int top,ll[N],rr[N],sk[N],ans;
    signed main()
    {
        scanf("%s",s+1);
        n=strlen(s+1);
        getsa(n,s);
        getht(n,s);
        top=1,sk[1]=1;
        for(int i=2;i<=n;i++)
        {
            while(top&&ht[sk[top]]>ht[i])
                rr[sk[top]]=i,top--;
            ll[i]=sk[top];
            sk[++top]=i;
        }
        while(top)
            rr[sk[top]]=n+1,top--;
        ans=n*(n-1)*(n+1)*1ll/2;
        for(int i=1;i<=n;i++)
            ans-=2*(i-ll[i])*(rr[i]-i)*ht[i];
        printf("%lld\n",ans);
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    P3809 【模板】后缀排序

    对各个下标i的后缀进行排序后,输出排名为i的首字符的下标

    #include 
    #define int long long
    #define endl '\n'
    
    using namespace std;
    const int N=1e6+5;
    const int mod=1e9+7;
    int n,k;
    int rk[N],rk2[N];   //以i开头后缀的排名
    char s[N];
    int sa[N];   //表示sa[i]表示排名i的后缀的开头下标
    
    //求解各个以i为起始下标的后缀字符串的排名
    bool cmp(int i,int j)
    {
        if(rk[i]!=rk[j])
            return rk[i]<rk[j];
        int ri=(i+k<=n ? rk[i+k]:-1);
        int rj=(j+k<=n ? rk[j+k]:-1);
        return ri<rj;
    }
    void getsa(int n,char *str)
    {
        for(int i=1;i<=n;i++)
            sa[i]=i,rk[i]=s[i];  //利用ASCLL码
        for(k=1;k<=n;k*=2)
        {
            sort(sa+1,sa+1+n,cmp);
            rk2[sa[1]]=1;
            for(int i=2;i<=n;i++)
                rk2[sa[i]]=rk2[sa[i-1]]+cmp(sa[i-1],sa[i]);
            for(int i=1;i<=n;i++)
                rk[i]=rk2[i];
        }
    }
    
    int ht[N];   //维护数组rk相邻两个后缀的lcp(i-1和i的最长公共前缀)
    void getht(int n,char *s)
    {
        for(int i=1;i<=n;i++)
            rk[sa[i]]=i;
        int h=0;
        ht[1]=0;
        for(int i=1;i<=n;i++)
        {
            int j=sa[rk[i]-1];
            if(h>0)
                h--;
            for(;j+h<=n&&i+h<=n;h++)
                if(s[j+h]!=s[i+h])
                    break;
            ht[rk[i]]=h;
        }
    }
    int top,ll[N],rr[N],sk[N],ans;
    signed main()
    {
        scanf("%s",s+1);
        n=strlen(s+1);
        getsa(n,s);
        for(int i=1;i<=n;i++)
            printf("%lld ",sa[i]);
        printf("\n");
        //getht(n,s);
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
  • 相关阅读:
    php+mysql物流信息网站
    大数据Hadoop之——Kafka API介绍与实战操作
    ortp 交叉编译
    Ray+GPU支持高性能计算
    每日三题 9.06
    【全志T113-S3_100ask】1-编译buildroot初体验
    【buildroot】buildroot使用笔记-04 | 重构的规则和方法
    【云原生 | 32】Docker运行数据采集和分析引擎Elasticsearch
    Layout工程师们--Allegro X AI实现pcb自动布局布线
    『力扣每日一题16』:存在重复元素
  • 原文地址:https://blog.csdn.net/weixin_51934288/article/details/126005292