zoukankan      html  css  js  c++  java
  • stone/reverse/string/digit(完美消除)

    stone/reverse/string/digit(完美消除)

    stone:

    【问题描述】

      平平去海边度假,海边有一片美丽的鹅卵石滩。平平在鹅卵石滩上捡了 $n$ 块美丽的 鹅卵石,并把它们排成一个序列,其中排第 i 位的鹅卵石的美丽度为 a_i。平平想从里面 按照原序列的顺序挑选出一个鹅卵石的子序列,使得在这个子序列里的后一块鹅卵石的美 丽度不比前一块低。平平还想知道,他这么做能得到的最长的子序列长度是多少。 平平想了想,认为这个问题很 naive,于是他决定将上述鹅卵石序列首尾相连,组成 一个鹅卵石环,然后计算在这个环上的满足要求的最长子序列的长度。

    【输入】 输入第一行包含一个整数 T,代表数据组数。 接下来有 2T 行,每两行代表一组数据。 每组数据的第一行包含一个整数 n ,代表鹅卵石的个数。 第二行包含 n 个非负整数,依次是 a_1 到 a_n,代表 n 个鹅卵石各自的美丽程度。 保证所有数据随机生成。具体地,每一项 a_i 独立地从 [1,n] 内的整数中等概率选 取。

    【输出】 输出共 T 行,每行一个正整数 m,表示该组数据中最长的满足要求的子序列的长度。

    【输入样例】

      2

      3

      3 1 2

      10

      1 3 8 8 1 7 9 3 10 10

    【输出样例】

      3

      7

    【输出说明】 对第一组数据,选取的子序列为 a_2, a_3, a_1 。

      1<=n<=1e4,t=10;

    先破环成链

    设DP数组dp[i][j]=k 表示区间[i,j]的最长不下降子序列期望长度为k

    但是发现这样会(T+M)LE

    区间随机生成下数据的最长不下降子序列期望长度为$sqrt{n}$,

    所以我们可以将dp方城写成dp[i][k]=j,表示右端点为i,最长不下降子序列长度为k的最右左端点为j,用树状数组维护即可

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define rep(i,a,b) for(register int i=a;i<=b;i++)
    #define lowbit(x) x&(-x)
    using namespace std;
    int n,t,a[20005],dp[20005][208],c[10050],res;
    inline int read(){
        int x=0,f=1;
        char ch=getchar();
        while('0'>ch || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
        while('0'<=ch && ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
        return x*f;
    }
    struct Tree{
        inline void upt(int x,int y){while(x<=n) c[x]=max(c[x],y),x+=lowbit(x);}
        inline int find(int x){
            int ans=0;
            while(x) ans=max(ans,c[x]),x-=lowbit(x);
            return ans;
        }
    }Q;
    int main(){
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n); res=0;
            memset(dp,0,sizeof(dp));
            rep(i,1,2*n) dp[i][1]=i;
            rep(i,1,n) a[i]=read(),a[i+n]=a[i];
            rep(j,2,207){
                memset(c,0,sizeof(c));
                rep(i,1,2*n){
                    int zlk=Q.find(a[i]);
                    if(i-zlk<n && zlk) dp[i][j]=zlk,res=j;
                    if(dp[i][j-1]) Q.upt(a[i],dp[i][j-1]); 
                }
            }
            printf("%d
    ",res);
        }
        return 0;
    }

    reserve:

    题目大意:求将环上任意一个区间翻转后,环上的最大区间和(n<=1e6)

    本题可以转换为求任意两个连续区间的最大和

    首先处理链上的情况:dp[i][j][0/1]表示前i个数选了j个区间,本数选没选,推下DP方程

    之后我们只需再强智选环的环首尾,再跑遍dp即可

    /*
    通过翻转,我们可以使换上任意两个连续区间并成一个区间
    所以答案显然是换上两个最大的连续区间的和
    分为两种情况
    1.强制选还上首尾的区间 
    2.区间不包含环的首尾,直接在链上DP 
    dp[i][j][k]表示到第i位,选了j个子段的最大值 ,第i个数选没选(k=0/1)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    #define int long long
    using namespace std;
    int n,dp[200040][3][2],a[200005],maxn=-1000000000,b;
    signed main(){
        scanf("%lld",&n);
        rep(i,1,n){
            scanf("%lld",&a[i]),maxn=max(maxn,a[i]);
            if(a[i]>=0) b++;
        }
        if(b<2){printf("%lld",maxn); return 0;}
        memset(dp,128,sizeof(dp));
        dp[0][0][0]=0;
        rep(i,1,n){
            rep(j,0,2){
                if(j>=1) dp[i][j][1]=max(dp[i-1][j][1]+a[i],max(dp[i-1][j-1][0]+a[i],dp[i-1][j-1][1]+a[i]));
                dp[i][j][0]=max(dp[i-1][j][1],dp[i-1][j][0]);
            }
        }
        maxn=max(dp[n][2][0],dp[n][2][1]);
        memset(dp,128,sizeof(dp));
        dp[1][0][1]=a[1];
        rep(i,2,n){
            rep(j,0,2){
                if(j>=1) dp[i][j][1]=max(dp[i-1][j][1]+a[i],max(dp[i-1][j-1][0]+a[i],dp[i-1][j-1][1]+a[i]));
                else dp[i][j][1]=dp[i-1][j][1]+a[i];
                dp[i][j][0]=max(dp[i-1][j][1],dp[i-1][j][0]);
            }
        }
        printf("%lld",max(maxn,dp[n][2][1]));
        return 0;
    }

    String:

    【问题描述】

    给出一个长度为 2N 的数字串,这个数字串中的每一位都是 0-9 的整数,其中,有一 些位置上的数是我们已知的,还有一些位置上的数未知,当且仅当这个数字串满足:前 N 个数码的乘积等于后 N 个数码的乘积的时候,我们称这个数字串是一个好的数字串,给出 一个有若干个位置未知的数字串,请你求出在未知处填上数码之后,使得这个串是一个好 的串的方案数

    【输入】 输入第一行包含一个整数 N 第二行一个长度为 2N 的数字串,其中有一些位置上为问号,代表这些位置上的字符未 知。

    【输出】 所求的方案数

    【输入样例】

       2

       2??3

    【输出样例】

       4

    【样例说明】

      (3,2) (6,4), (9,6), (0,0) 不难证明没有其它的解

    【数据规模】

      对于 30%数据:1≤n≤4

      对于所有数据:1≤n≤18

      比较简单的数位DP

      将0~9中的质数列出来,设一个五维DP,随便推推dp式子

       

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define int long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    int dp[40][60][40][30][30],n,b1,b2,a1,a2,res,b[10]={0,0,1,0,2,0,1,0,3,0};
    int c[10]={0,0,0,1,0,0,1,0,0,2},d[10]={0,0,0,0,0,1,0,0,0,0},e[10]={0,0,0,0,0,0,0,1,0,0};
    int ksm(int x,int y){
        int r=1;
        while(y){
            if(y&1) r=(r*x);
            x*=x;
            y>>=1;
        }
        return r;
    }
    
    int C(int x,int y){
        int res=1;
        rep(i,y-x+1,y) res*=i;
        rep(j,1,x)res/=j;
        return res;
    }
    char a[40];
    signed main(){
    //    freopen("string.out","w",stdout);
        scanf("%lld%s",&n,a+1);
        dp[0][0][0][0][0]=1;
        rep(i,1,n){
            if(a[i]=='0') ++b1;
            if(a[i]=='?') ++b2;
        }
        rep(i,n+1,n*2){
            if(a[i]=='0') ++a1;
            if(a[i]=='?') ++a2;
        }
        int ans=0,ans2=0;
        rep(i,1,a2)    ans=ans+C(i,a2)*ksm(9,a2-i);
        if(a1) ans+=ksm(9,a2);
        rep(i,1,b2)    ans2=ans2+C(i,b2)*ksm(9,b2-i);
        if(b1) ans2+=ksm(9,b2);
        if(a1 || b1){
            printf("%lld",ans2*ans);
            return 0;
        }
        res=ans*ans2;
        rep(i,1,2*n){
            rep(j,0,54){
                rep(k,0,36){
                    rep(l,0,18){
                        rep(m,0,18){
                            if(i<=n){
                                if(a[i]!='?'){
                                    if(j-b[a[i]-'0']>=0 && k-c[b[a[i]-'0']]>=0 && l-d[b[a[i]-'0']]>=0 && m-e[b[a[i]-'0']]>=0) dp[i][j][k][l][m]=dp[i-1][j-b[a[i]-'0']][k-c[a[i]-'0']][l-d[a[i]-'0']][m-e[a[i]-'0']];
                                    else dp[i][j][k][l][m]=0;
                                    continue;
                                }
                                rep(t,1,9)    if(j-b[t]>=0 && k-c[t]>=0 && l-d[t]>=0 && m-e[t]>=0){
                                    dp[i][j][k][l][m]+=dp[i-1][j-b[t]][k-c[t]][l-d[t]][m-e[t]];
                                }
                            }
                            else{
                                if(a[i]!='?') dp[i][j][k][l][m]=dp[i-1][j+b[a[i]-'0']][k+c[a[i]-'0']][l+d[a[i]-'0']][m+e[a[i]-'0']];
                                else rep(t,1,9) dp[i][j][k][l][m]+=dp[i-1][j+b[t]][k+c[t]][l+d[t]][m+e[t]];
                            }
                        }
                    }
                }
            }
        }
        cout<<dp[2*n][0][0][0][0]+res;
        return 0;
    }

    Digit:

    【问题描述】

    对于一个正整数n,定义一个消除操作为选定[L,R,x],如果n的第L到第R位上的数 字都≥x,并且这些数都相等,那么该操作合法(从低位到高位编号从1开始),并将这些位数 上的数减x。 定义n的最小操作数为对一个数操作最少的次数使得这个数变成0的次数。如1232的最 小操作数为3,一个合法解是[2,2,1],[1,3,2],[4,4,1]。 试求[L,R]内最小操作数为k的数的个数。

    【输入】 输入为三个正整数L、R、k。

    【输出】 输出答案。

    【输入输出样例】

      digit.in

      10 21 2

      digit.out

      9

    【输入输出样例解释】 [10,21]区间内除10、11、20是最小操作数为1的以外均是最小操作数为2。

    【数据范围】 对于30%的数据,1≤L≤R≤107。 对于70%的数据,1≤L≤R≤109。 对于100%的数据,1≤k≤109,1≤L≤R≤1018。 保证数据有一定梯度。

    对于一个数字,我们按位将它拆分,发现如果第i位(从左往右)大于第i-1位,那么这一位肯定要多进行一次操作,使他砍到和第i-1位一样高,所以这就是一个单调栈,反之就将栈内比它大的数弹出即可

    设dp[i][j][k]表示第计算到第i位,状态为j,需进行k次操作的数的数量,状态即为栈内有(1<<i)或没有(0<<i)该数,跑数位DP

    CODE:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define int long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    int dp[21][2025][21],tot,m[21],pre[2025][21],k;
    int wk(int y){if(y<0) return 0;return 1<<y;}
    int DP(int x,int y,int t,int z){
        if(t>k) return 0;//cout<<"H";
        if(x==0 && t==k) return 1;
        else if(x==0) return 0;
        if(!z && dp[x][y][t]!=-1) return dp[x][y][t];
        int ans=0,en=z?m[x]:9;
        rep(i,0,en){
            int hh=t;if(!(y&(wk(i-1))) && i) ++hh;
            ans+=DP(x-1,pre[y][i],hh,z && en==i);
        }
        if(!z) dp[x][y][t]=ans;
        return ans;
    }
    int cnt(int x){
        tot=0;memset(dp,-1,sizeof(dp));
        while(x){m[++tot]=x%10; x/=10;}
        return DP(tot,0,0,1);
    }    
    signed main(){
    //    freopen("digit.out","w",stdout);
        int l,r; scanf("%lld%lld%lld",&l,&r,&k);
        rep(i,0,2024) rep(j,0,9) pre[i][j]=(((1<<j)-1)&i)|wk(j-1);
        printf("%lld",cnt(r)-cnt(l-1));
        return 0;
    }
  • 相关阅读:
    How can i install ctags in centos 6.4
    [转载] Ubuntu Vim powerline 插件
    Vim 相关网页
    [转载] vim技巧:设置空格和Tab字符可见
    Don't trust cplusplus.com, it's crap. If any, go to cppreference.com.
    Vim yank only 50 lines
    按进程名终止进程
    Shell 脚本 Tips
    Bash 脚本 逐行处理文本文件的内容
    生成并配置https本地证书
  • 原文地址:https://www.cnblogs.com/handsome-zlk/p/11272031.html
Copyright © 2011-2022 走看看