zoukankan      html  css  js  c++  java
  • 国庆 day 6 上午

    1. 角谷猜想
    (kakutani.pas/c/cpp)
    (kakutani.in/out)
    时间限制:1s/空间限制:256M
    【题目描述】
        某个名字末尾是 654321 的小 A 同学是个大家眼中公认的学霸(虽然他永远
      不承认),他对题目的直觉到了一种可怕的地步,一眼看出题目的算法对他而言
      只是小 Case,他甚至能在看到一个证明的瞬间敏锐地判断出这个证明的真伪。
      现在小 A 同学机缘巧合地看到了角古猜想 (即对于 x 当它为奇数则 1 3   x x ,
      为偶数,则
      2
      x
      x  ,一直重复这个步骤,则 x 最终会变为 1),在看完这个猜想的
       一瞬间,他的直觉就来了——他认为角古猜想一定是错的!然后——他立刻就能
      找出反例!
        他立刻在纸上写满了 ) 00 10 1 (   n n 个小于 ) 10 0 ( 10
      4
        L
      L
      的正整数, 打算放
      到他的 grand super computer 上去跑,可是他突然觉得有些正整数不是很吉利,
      可能会干扰到他的最终结果,所以他打算把一些正整数加工一下。
        小 A 觉得 4、7、13 都是不吉利的数字,所以要把所有正整数里的 4、7、13
      都去掉,如果去掉后得到的新数字里依旧有 4、7、13,那么就要继续删掉,直
      到最后的数组不存在 4、7、13,它才是一个吉利的数字。
        例如 1 => 113 => 11133 => 111733 => 1411733
        特别规定,如果最后所有数字都被删掉了,就输出 0
        小A觉得这个枯燥的工作不适合他这样的天才, 于是就把这个工作交给了你。
      当然, 只要你能顺利解决,小 A 承诺会在那篇将会震惊世界的论文的特别感谢栏
      上署上你的大名。
    【输入格式】 (kakutani.in)
      一共 1  n 行。
      第一行一个正整数 ) 0 10 1 (   n n ,表示数字个数。
      接下来每行一个正整数 x 。
    【输出格式】 (kakutani.out)
      一共 n 行。
      每行一个正整数,表示输入每个 x 对应的答案。
    【输入输出样例】
      kakutani.in kakutani.out
      5
      13713
      141713
      1333333372589
      1411733
      2147483647
      0
      11
      3333332589
      1
      21836
    【数据规模约定】
      对于 10% 的数据, 2147483647 0   x
      对于另外 10% 的数据,给定的数字 x 没有数码 3
      对于另外 10% 的数据, 1  n
      对于全部的数据, ) 00 10 1 (   n n ,
    Lx 10 1   )

     思路:栈维护一下。

     吐槽:学校的机子跑的太慢了,竟然卡我输出。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,bns[101000],vis[101000];
    char num[101000];
    int main(){
        freopen("kakutani.in","r",stdin);
        freopen("kakutani.out","w",stdout);
        scanf("%d",&n);
        while(n--){
            scanf("%s",num);
            int len=strlen(num),pos=0,ff=0;
            memset(vis,0,sizeof(vis));
            for(int i=0;i<len;i++){
                if(num[i]=='4')    vis[i]=1;
                else if(num[i]=='7')    vis[i]=1;
                else if(num[i]=='1')    bns[++pos]=i;
                else if(num[i]=='3'&&pos){
                    vis[i]=1;
                    vis[bns[pos--]]=1;
                }
                else    pos=0;
            }
            for(int i=0;i<len;i++)
                if(!vis[i]){
                    ff=1;
                    putchar(num[i]);    
                }
            if(!ff)    putchar('0');
            printf("
    ");
        }
    }
    /*
    4
    123131313473
    */
    View Code

    2. 刀塔
    (dota.pas/c/cpp)
    (dota.in/out)
    时间限制:2s/空间限制:256M
    【题目描述】
        事情要从小 A 的朋友小 S 说起,小 S 是个刀塔狂热粉,他每天除了学习就是
      在打刀塔。然而让小 S 很苦恼的事情是,他发现最近他似乎遇见瓶颈了,他发现
      他每次输的时候都是雪崩, 赢的时候都是躺赢, 完全发挥不出自己应该有的实力。
      他上贴吧请教了三分钟辉耀羊刀的万分大神,接到了万分大神的圣旨:去看自己
      的录像反省反省。于是小 S 决定利用十一假期的时间好好反省反省。
        小 S 调出了自己前段时间的游戏录像,一共有 N 个录像,他给每个录像一个
      正整数表示这个录像的观看价值。 现在他决定从里面找到二组连续的录像来观看,
      这两组录像的要求如下:
        1.两组录像里每一组的录像数量都不能小于 A,不然没有意义
        2.他看的总录象的数量一定要超过 B,他相信看的越多就越好
        3.小 S 觉得两组录像时间隔的太近没有意义,因为很有可能前后两段暴露的
        问题基本一致,但是他又觉得隔的太远也干扰他去思考自己当时的状态。所以他
      要求这两段录像中间应该刚好隔了 K 个录像
        好吧,这已经够让人烦躁的了,但是小 S 还不满足,他觉得这样的挑选方案
      依旧很多,所以他想挑选足够好的方案,一个质量足够高的方案——也就是观看
      价值最低的录像的观看价值要尽可能的高。
    【输入格式】 (dota.in)
      一共 2 行。
      第一行四个正整数 K B A N 、 、 、 。
      接下来一行 N 个正整数,表示每个录像的观看价值 ) 10 1 (
      9
        x x 。
    【输出格式】 (dota.out)
      一共 1 行,表示最佳方案中观看价值最低的录像的观看价值。
    【输入输出样例 1】
      dota.in dota.out
      10 2 5 3
      7 8 2 3 1 6 4 10 5 9
      4
    【输入输出样例 2】
      dota.in dota.out
      20 3 9 3
      54867025 259306632 473619223 170507035
      347936959 421059860 246006182 948910354
      630205869 541359081 574152766 665959900
      843439075 445125437 774018043 719562887
      705993886 133173428 256457367 708196876
      246006182
    【数据规模约定】
      保证 B A A   , N K B  
      对于 10% 的数据, 1000 100   N
      对于另外 10% 的数据, B A A  
      对于另外 30% 的数据, 100  B
      对于全部的数据, ) 10 100 (
      6
        N N , 0 000 10 10   K B,
    【样例解释】
      对于样例 1,最优方案为选择 7 8 2 3 1 6 4 10

    40分暴力

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 1000010
    using namespace std;
    int x,y;
    int asd,qwq;
    int A,B,k,ans;
    int t,q,n,mx,nm;
    int a[N];
    int stmin[N][20],mn[N];
    void init(){
        mn[0]=-1;
        for(int i=1;i<=n;i++){
            mn[i]=((i&(i-1))==0)?mn[i-1]+1:mn[i-1];
            stmin[i][0]=a[i];
        }
        for(int j=1;j<=mn[n];j++)
            for(int i=1;i+(1<<j)-1<=n;i++)
                stmin[i][j]=min(stmin[i][j-1],stmin[i+(1<<(j-1))][j-1]);
    }
    int rmq_min(int L,int R){
        int k=mn[R-L+1];
        return min(stmin[L][k],stmin[R-(1<<k)+1][k]);
    }
    int main(){
        freopen("dota.in","r",stdin);
        freopen("dota.out","w",stdout);
        scanf("%d%d%d%d",&n,&A,&B,&k);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        init();
        for(int i=1;i<=n-B-k+1;i++){
            mx=B-A;
            for(int j=A;j<=mx;j++){
                nm=B-j;
                asd=rmq_min(i,i+j-1);
                qwq=rmq_min(i+j+k,i+j+k+nm-1);
                ans=max(ans,min(asd,qwq));
            }
        }
        cout<<ans;
    }
    View Code

    正解思路:

    以下解题思路来自xxy大佬的博客

    枚举i为间隔K个录像的左端点,那么间隔录像为[i,i+k-1]

    设第一段为[Sa,Ta],第二段为[Sb,Tb],Ma为min[Sa,Ta],Mb为min[Sb,Tb]

    随着Sa的左移,Ma单调不增

    随着Tb的右移,Mb单调不增

    如果枚举Sa,则有以下式子: Ta-Sa+1+Tb-Sb+1>=B

    即Tb>=B+Sa+Sb-Ta-2

    因为Tb右移,Mb单调不增,所以Tb取等号最优

    所以二分Sa的位置,用st表查询Ma,Mb

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define MAXN 1000001
    using namespace std;
    int n,p,k,A,B;
    int logg2[MAXN];
    int st[MAXN][21];
    void stpre(){
        p=log2(n);
        for(int j=1,k=2;j<=p;j++,k<<=1)
            for(int i=1;i+k-1<=n;i++)
                st[i][j]=min(st[i][j-1],st[i+(k>>1)][j-1]);
            for(int i=1;i<=n;i++)    logg2[i]=log2(i);
    }
    int getmin(int s,int t){
        p=logg2[t-s+1];
        int len=1<<p;
        return min(st[s][p],st[t-len+1][p]);
    }
    int main(){
        freopen("dota.in","r",stdin);
        freopen("dota.out","w",stdout);
        scanf("%d%d%d%d",&n,&A,&B,&k);
        for(int i=1;i<=n;i++)
            scanf("%d",&st[i][0]);
        stpre();
        int Sa,Sb,Ta,Tb,Ma,Mb,Mi;
        int ans=0;
        int l,r,mid,tmp;
        for(int i=A+1;i<=n-A-k+1;i++){
            Ta=i-1;
            Sb=i+k;
            Tb=max(Sb+A-1,B+i-A+Sb-Ta-2);
            Ma=getmin(i-A,Ta);
            Mb=getmin(Sb,Tb);
            l=1;r=i-A;tmp=min(Ma,Mb);
            while(l<=r){
                mid=(l+r)/2;
                Tb=max(Sb+A-1,B+mid+Sb-Ta-2);
                Ma=getmin(mid,Ta);
                Mb=getmin(Sb,Tb);
                tmp=max(tmp,min(Ma,Mb));
                if(Ma<Mb)    l=mid+1;
                else if(Ma==Mb)    break;
                else r=mid-1;
            }
            ans=max(ans,tmp);
        }
        printf("%d",ans);
    }
    View Code

    3. 反击数
    (spenum.pas/c/cpp)
    (spenum.in/out)
    时间限制:2s/空间限制:256MB
    【题目描述】
        上次说到小 A 在你的帮助下,在反证角谷猜想的路上已经看见了曙光,他相
      信自己即将为这个著名难题画上最后的句点,小 A 十分地兴奋,结果他那微不足
      道的老毛病又犯了——他忍不住想炫耀一番,结果搞得朋友圈内人尽皆知,当然
      也就传到了另一位学霸小 H 的耳中。
        虽然表面上小 H 和小 A 是挚友,但其实不难想象,这两个天才在私下里早已
      将对方视为此生必须要战胜的对手。和天赋异禀的小 A 不同,小 H 拥有的是逆天
      的气运——经常他选择题都不需要看选项就能全对 (据说这也是曾经笃信唯物主
      义的小 A 为什么变得神经质般迷信的根本原因) 现在小 H 要正式向小 A 在角谷猜
      想上发起反击, 不过两个学霸在某一点上倒是达成了一致——这个猜想一定是错
      的,所以它必然有反例!
        对自己的气运抱有足够自信的小 H 打算用这样的方式来找到反例:他先选定
       一个自己的幸运数 X,他认为所有中间出现了 X 的数都是“扩展幸运数” (包括
      X 自身) (例:若 X=69 那么 84576901 就是一个“扩展幸运数” ,在 84576 后面
      出现了数码 69,而 679 则不是一个“扩展幸运数” ),然后小 H 会再精心挑选一
      个“命运数”K,最后小 H 将在随机生成的正整数区间[L, R]中选择第 K 大的“扩
      展幸运数” ,用它去验证角谷猜想。小 H 认为这样的速度一定快过小 A 那个落后
      的办法。
        好了,现在同样的工作摆在了你的面前,你需要帮助小 H 得到那个数字。
       PS:小 H 有时候会突然无征兆地打瞌睡(为了保养他的运气) ,所以可能正
      整数区间[L, R]中并没有 K 个“扩展幸运数” ,这时候输出“Hey,wake up!” (不
      含引号)
    【输入格式】 (spenum.in)
      输入仅有一行,共 4 个数字,按次序分别为 L,R,X,K
    【输出格式】 (spenum.out)
      输出为一行,即正整数区间[L, R]中第 K 大的“扩展幸运数”
    【输入输出样例 1】
      spenum.in spenum.out
      1 1000 6 14 67
    【输入输出样例 2】
      spenum.in spenum.out
      1 1 2 1 Hey,wake up!
    【样例解释】
      前 14 个数字分别是 6、16、26、36、46、56、60、61、62、63、64、65、66、67
    【数据规模约定】
      对于 10% 的数据 : 1000000 1    L R
      对于另外 10% 的数据 : 1  K
      对于另外 20% 的数据 : 9 1   X
      对于 100% 的数据 :
      18
      10 , , , 1   K X R L

    思路:

    数位DP

    dp[i][j][0/1] 前i位匹配到X的第j位,是否已经包含1个X的数的个数

    二分,计算<=mid的数里的答案

    其中的匹配用kmp

    这个dp的清空可以只在最开始清空一次。

    原因在这里:

    第一:memset(dp,-1,sizeof dp);放在多组数据外面。
    
    这一点是一个数位特点,使用的条件是:约束条件是每个数自身的属性,而与输入无关。
    具体的:上一题不要62和4,这个约束对每一个数都是确定的,就是说任意一个数满不满足这个约束都是确定,比如444这个数,它不满足约束条件,不管你输入的区间是多少你都无法改变这个数不满足约束这个事实,这就是数自身的属性(我们每组数据只是在区间计数而已,只能说你输入的区间不包含444的话,我们就不把它统计在内,而无法改变任何事实)。
    由此,我们保存的状态就可以一直用(注意还有要limit,不同区间是会影响数位在有限制条件下的上限的)
    这点优化就不给具体题目了,这个还有进一步的扩展。不过说几个我遇到的简单的约束:
    1.求数位和是10的倍数的个数,这里简化为数位sum%10这个状态,即dp[pos][sum]这里10 是与多组无关的,所以可以memset优化,不过注意如果题目的模是输入的话那就不能这样了。
    2.求二进制1的数量与0的数量相等的个数,这个也是数自身的属性。
    3.。。。。。
    还是做题积累吧。搞懂思想!
    下面介绍的方法就是要行memset优化,把不满足前提的通过修改,然后优化。
    介绍之前,先说一种较为笨拙的修改,那就是增加状态,前面讲limit的地方说增加一维dp[pos][state][limit],能把不同情况下状态分别记录(不过这个不能memset放外面)。基于这个思想,我们考虑:约束为数位是p的倍数的个数,其中p数输入的,这和上面sum%10类似,但是dp[pos][sum]显然已经不行了,每次p可能都不一样,为了强行把memset提到外面加状态dp[pos][sum][p],对于每个不同p分别保存对应的状态。这里前提就比较简单了,你dp数组必须合法,p太大就G_G了。所以对于与输入有关的约束都可以强行增加状态(这并不代表能ac,如果题目数据少的话就随便你乱搞了)
    View Code

    或者戳这里

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int len;
    char X[20];
    long long L,R,K;
    int net[20],num[20];
    long long l,r,mid,ans;
    long long dp[20][20][2];
    void KMP(){
        len=strlen(X);
        int j;
        for(int i=1;i<len;i++){
            j=net[i];
            while(j&&X[i]!=X[j])    j=net[j];
            net[i+1]=X[i]==X[j]?j+1:0;
        }
    }
    long long dfs(int pos,int stact,bool limite,bool get){
        if(pos==-1)    return get;
        if(!limite&&dp[pos][stact][get]!=-1)    return dp[pos][stact][get];
        int up=limite?num[pos]:9;
        long long tmp=0;
        int j;
        for(int i=0;i<=up;i++){
            j=stact;
            while(j&&X[j]-'0'!=i)    j=net[j];
            if(X[j]-'0'==i)    j++;
            tmp+=dfs(pos-1,j,limite&&(i==up),get||(j==len));
        }
        if(!limite)    dp[pos][stact][get]=tmp;
        return tmp;
    }
    long long slove(long long now){
        int pos=0;
        while(now){
            num[pos++]=now%10;
            now/=10;
        }
        return dfs(pos-1,0,1,0);
    }
    int main(){
    
        freopen("spenum.in","r",stdin);
        freopen("spenum.out","w",stdout);
        scanf("%I64d%I64d%s%I64d",&L,&R,X,&K);
        KMP();
        memset(dp,-1,sizeof(dp));
        long long tmp=slove(L-1);
        if(slove(R)-slove(L-1)<K){
            puts("Hey,wake up!");
            return 0;
        }
        l=L;r=R;
        while(l<=r){
            mid=(l+r)/2;
            if(slove(mid)-tmp<K)    l=mid+1;
            else{
                ans=mid;
                r=mid-1;
            }
        }
        printf("%I64d",ans);
    }
    View Code
    细雨斜风作晓寒。淡烟疏柳媚晴滩。入淮清洛渐漫漫。 雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。
  • 相关阅读:
    UNIX常用shell
    exit函数
    linux消息队列
    互斥量
    RCS版本控制
    linux samba
    UML建模
    linux syslog
    python基础-列表List及内置方法
    仿美团详情页与购物车源码-详情页
  • 原文地址:https://www.cnblogs.com/cangT-Tlan/p/7644767.html
Copyright © 2011-2022 走看看