zoukankan      html  css  js  c++  java
  • 【简】题解 吉首大学第十一届“新星杯”大学生程序设计大赛

    传送门:OJ

    OJ

    前言

    A K题的大模拟实在写不动了 摸掉了(其实是其他作业叠太多了)等有空了再说吧

    写得太烂  大佬们请不要介意

    问题 A 咱俩下象棋去:

    给你一个象棋盘  棋子的个数可能跟普通的不同 问能否在2步内将对方的军

    QWQ:

    考场A掉的大佬tql

    考场一看是到大模拟 果断跳

    update:2021/12/13 20:47

    后来一时兴起还是做了   (主要还是因为在逃避外教大作业)虽然作业也还没写 

    写10:57-----12:37             调12:37-----14:14 update12     19:00----20:30 update34

    注意看清题目 指定一个棋子走两次 不知道我当时怎么理解的 拒绝关键时候脑内毒瘤断句

    实现的时候精简了下代码 所有移动都用方向数组实现   合法 改图 还原 传递  都放进了res函数

    方向数组可以减少手残的基数  虽然我还是错了那么多 代码能力还是太弱了

    感谢群内管理员学长抽时间帮忙查错  不然可能整个晚上也没了

    代码中可能还有很多暗病

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    const int N=1e2+10;
    int n=10,m=9;
    char a[N][N];
    int dxk[5]={0,0,-1,0,1},dyk[5]={0,-1,0,1,0};//左下右上 
    int dxs[5]={0,-1,-1,1,1},dys[5]={0,-1,1,-1,1};
    int dxb[5]={0,-1,0,0,0},dyb[5]={0,0,-1,1,0};
    int dxm[9]={0,-1,-1,-2,-2,1,1,2,2},dym[9]={0,-2,2,-1,1,-2,2,-1,1};
    int dxo[9]={0,0,0,-1,-1,0,0,1,1},dyo[9]={0,-1,1,-0,0,-1,1,-0,0};
    inline int pdc(int x,int y)
    {
    //    if(a[x][y]=='H') return -1;
        if('A'<=a[x][y]&&a[x][y]<='Z'&&a[x][y]!='H') return 0;
        if('a'<=a[x][y]&&a[x][y]<='z'&&a[x][y]!='H') return 1;
        return -1;
    }
    inline bool pdf(int tx,int ty,int xl,int xr,int yl,int yr,int o)
    {
        if(xl<=tx&&tx<=xr&&yl<=ty&&ty<=yr&&(pdc(tx,ty)!=o)) return 1;//tx---ty //update 2
        return 0;
    }
    inline void work(int cs,int qx,int qy);
    inline void res(int x,int y,int tx,int ty,int xl,int xr,int yl,int yr,int o,int cs)
    {
        
        if(pdf(tx,ty,xl,xr,yl,yr,o))
        {
            char tmpv=a[tx][ty];
            a[tx][ty]=a[x][y];a[x][y]='H';
            if(tmpv=='K'){printf("yes");exit(0);}
            work(cs+1,tx,ty);
            a[x][y]=a[tx][ty];a[tx][ty]=tmpv;
        }
    }
    inline void work(int cs,int qx,int qy)
    {
        if(cs>2) return;
        for(int x=1;x<=n;x++)
        for(int y=1;y<=m;y++) 
        if(pdc(x,y)==1)
        {
            if(cs==2&&(x!=qx||y!=qy)) continue;//update 3
            if(a[x][y]=='k') 
            {
                for(int k=1;k<=4;k++)
                {
                    int tx=x+dxk[k],ty=y+dyk[k];
                    res(x,y,tx,ty,8,10,4,6,1,cs);
                }
            }
            if(a[x][y]=='s')
            {
                for(int k=1;k<=4;k++)
                {
                    int tx=x+dxs[k],ty=y+dys[k];
                    res(x,y,tx,ty,8,10,4,6,1,cs);
                }
            }
            if(a[x][y]=='b') 
            {
                int edk=1;
                if(x<=5) edk=3;
                for(int k=1;k<=edk;k++)
                {
                    int tx=x+dxb[k],ty=y+dyb[k];//update 4  之前兵的移动数组打成士的了 
                    res(x,y,tx,ty,1,10,1,9,1,cs);
                }
            }
            if(a[x][y]=='c') 
            {
                int dxc=0,dyc=0;
                for(int k=1;k<=4;k++)
                for(dxc=dxk[k],dyc=dyk[k];1<=x+dxc&&x+dxc<=10&&1<=y+dyc&&y+dyc<=9;dxc+=dxk[k],dyc+=dyk[k])
                {
                    int tx=x+dxc,ty=y+dyc;
                    res(x,y,tx,ty,1,10,1,9,1,cs);
                    if(pdc(tx,ty)==0||pdc(tx,ty)==1) break;
                }
            }
            if(a[x][y]=='p')  
            {
                int dxc=0,dyc=0,gs=0;
                for(int k=1;k<=4;k++)
                for(gs=0,dxc=dxk[k],dyc=dyk[k];1<=x+dxc&&x+dxc<=10&&1<=y+dyc&&y+dyc<=9;dxc+=dxk[k],dyc+=dyk[k])
                {
                    int tx=x+dxc,ty=y+dyc;//update 1 x--y
                    if(pdc(tx,ty)==0||pdc(tx,ty)==1) gs++;
                    if(pdc(tx,ty)==-1&&gs==0) res(x,y,tx,ty,1,10,1,9,1,cs);
                    if(pdc(tx,ty)==0&&gs==2)  res(x,y,tx,ty,1,10,1,9,1,cs);
                    if(gs==2) break;
                }
            }
            if(a[x][y]=='x')
            {
                for(int k=1;k<=4;k++)
                {
                    int mx=x+dxs[k],my=y+dys[k];
                    if(pdc(mx,my)==0||pdc(mx,my)==1) continue;
                    int tx=x+dxs[k]*2,ty=y+dys[k]*2;
                    res(x,y,tx,ty,6,10,1,9,1,cs);
                }
            }
            if(a[x][y]=='m') 
            {
                for(int k=1;k<=8;k++)
                {
                    int mx=x+dxo[k],my=y+dyo[k];
                    if(pdc(mx,my)==0||pdc(mx,my)==1) continue;
                    int tx=x+dxm[k],ty=y+dym[k];
                    res(x,y,tx,ty,1,10,1,9,1,cs);
                }
            }
        }
    }
    int main()//写10:57-----12:37    调12:37-----14:14 update12   7:00----8:30 update34   千万好好读清题目 两次只能用一个棋子  还有其他老毛病 
    {
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          cin>>a[i][j];
        work(1,0,0);printf("no");
        return 0;
    }

    问题 B 抽积木游戏

    给你竖着的n个积木 当积木的长度能被高度加1整除可抽出

    给定积木堆 问是否能被抽完

    QWQ:

    显然上面的积木不会对下面积木造成影响

    对于某个高度的积木

    在最优抽出的过程中 一定能保证 对于其高度及其以下的位置 在某个时刻能够达到 

    那么 对于该积木 如果能被 其高度及其以下的 任意一高度整除 那么该积木就可抽出

    考虑从底层往上枚举  用cup记录该数能否被抽

    设高度为x  

    如果x为质数 那么 将x*Z 都可以取到

    如果非质数 那么一定被之前某个质数的更新所覆盖

    最终的流程就是

    线性筛预处理质数

    从底层往上枚举 

      判断该高度积木能否抽出

      更新cup标记能取到的值

    输出结果

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    const int N=2e6+10;
    int n,mx;
    int a[N],pd[N];
    int prime[N],top,vis[N];
    void xxs(int mx)//线性筛  
    {
        vis[0]=vis[1]=1; //1为不是0为是 
        for(int i=2;i<=mx;i++)
        {
            if(!vis[i]) prime[++top]=i;
            for(int j=1;j<=top&&prime[j]*i<=mx;j++)
            {
                vis[prime[j]*i]=1;
                if(!(i%prime[j])) break;
            }
        }
    }
    int main()
    {
        n=read();xxs(N-10); 
        for(int i=1;i<=n;i++) a[i]=read(),mx=max(mx,a[i]);
        for(int i=1;i<=n;i++)
        {
            int del=i+1;
            if(!vis[del]) 
            for(int j=del;j<=N-10;j+=del) pd[j]=1;
            if(!pd[a[i]]){printf("NO");return 0;}
        }
        printf("YES");
        return 0;
    }

    问题 C 梅贾的窃魂卷

    给个层数最高0层最高25次

    k +5次  d -10次 a -2次

    给定kda个数

    问有多少种排列方法使得最后层数为25次

    QWQ:

    刚开始被原题意误导说大于等于25次  以为层数能大于25 错了好几次 浪费了一段时间

    直接考虑dp  emmm不知道为什么我开始还怀疑了正确性 考虑正做还是反做 还是用广搜做

    数据范围有限 可直接设dp[i][j][k][v] 前三位表示各用了多少次 一维表示当前的值

    记得用max min 限制更新 时v更新时的上下界

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    const ll N=100;
    ll k,d,a,ans;
    ll dp[20][10][10][70];
    int main()
    {
        k=read(),d=read(),a=read();
        dp[0][0][0][0]=1;
        for(ll i=0;i<=k;i++)
        for(ll j=0;j<=d;j++)
        for(ll k=0;k<=a;k++) 
        for(ll v=0;v<=25;v++) 
        {
            dp[i+1][j][k][min(25*1ll,v+5*1ll)]+=dp[i][j][k][v];
            dp[i][j+1][k][max(0*1ll,v-10*1ll)]+=dp[i][j][k][v];
            dp[i][j][k+1][min(25*1ll,v+2*1ll)]+=dp[i][j][k][v];
        }
        for(ll i=25;i<=25;i++) ans+=dp[k][d][a][i];
        cout<<ans;
        return 0;
    }

    问题 D 强行数学题

    给2个式子求另个式子 

    QWQ:

    没什么好说的 真的强行数学题

    (2)*2-(1)=(3)

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    #define db double
    const db pi=3.1415926;
    int main()
    {
        printf("%.3lf",pi*pi/12.0);
        return 0;
    }

    问题 E 羊村保卫站

    给你个方阵的左上角位置和边长   给你个点

    问以改点为圆心 最小半径多少能覆盖矩阵

    QWQ:

    (毒)瘤)

    不得不提 该题的坐标轴x向右 y轴向上 不是默认的坐标轴

    考场考场反反复复看了题目好几遍  最开始还以为是贪心错了 看时间范围够把边长也都算了进去 

    时间消失法术 心态差点爆炸  我记住你了

    显然 只要保证方阵四个顶点能被包括就可以了 

    不够就算边长也算也不会超时

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    }
    #define db double
    ll xx,yy,d;
    ll x,y;
    db ans;
    inline db js(ll xx,ll yy,ll x,ll y)
    {
        return sqrt(1.0*abs(xx-x)*abs(xx-x)+1.0*abs(yy-y)*abs(yy-y));
    }
    int main()
    {
        xx=read(),yy=read(),d=read();
        x=read(),y=read();
    ////    cout<<js(xx,yy,x,y)<<endl;
    //    for(ll i=0;i<=d;i++) ans=max(ans,js(xx+i,yy,x,y));
    //    for(ll i=0;i<=d;i++) ans=max(ans,js(xx,yy+i,x,y));
    //    for(ll i=0;i<=d;i++) ans=max(ans,js(xx+d,yy+i,x,y));
    //    for(ll i=0;i<=d;i++) ans=max(ans,js(xx+i,yy+d,x,y));
        db tmp=
        max(
        max(js(xx+d,yy,x,y),js(xx,yy-d,x,y))
        ,
        max(js(xx,yy,x,y),js(xx+d,yy-d,x,y))
        );
        ans=max(ans,tmp);
        printf("%.3lf",ans);
        return 0;
    }

    问题 F 《特殊三角形》

    给你几个矩阵 观察规律

    求第n个矩阵

    QWQ:

    考场看也是大模拟也跳了下

    后来其他题目做差不多回来看只剩半小时 结束差几分钟才调出来

    最后这题发现交了总时间还是比前面的人总时间成 排名没变化

    观察得知 图形是三角形往左下和右下复制一遍重叠一部分

    按意模拟 冷静

    我的实现是按方阵copy记录 复制和被复制的两个矩阵的左上角位置 如果已经是 *就跳过防止覆盖

    设三角形边长*数为len           去0列后 矩阵中点位置w

    被复制 起始 stx      sty

    复制    起始  tmpx  tmpy

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    const int N=1300;
    int n,w,md;
    int stx,sty,len;
    char a[N][N];
    inline void cp(int x,int y,int len)
    {
        for(int i=0;i<len;i++)
        for(int j=0;j<2*len;j++)
        if(a[x+i][y+j]==' ')
          a[x+i][y+j]=a[stx+i][sty+j];
    }
    int main()
    {
        n=read();w=(1<<n)+1;
        for(int i=1;i<=w;i++)
        for(int j=0;j<=2*w-1;j++)
          if(a[i][j]!='*') a[i][j]=' ';
        a[1][w]='*';
        a[2][w-1];a[w][w-2]='*';
        stx=1,sty=w-1;len=(1<<0)+1;//*长 
        for(int cs=1;cs<=n;cs++)
        {
            int tmpx=stx+len-1,tmpy=sty-len+1;
            cp(tmpx,tmpy,len);
            tmpx=stx+len-1,tmpy=w;
            cp(tmpx,tmpy,len);
            stx=1;sty=sty-len+1;
            len=((1<<cs)+1); 
        }
        for(int i=1;i<=w;i++,puts(""))
        for(int j=0;j<=2*w-1;j++)
          cout<<a[i][j];
        return 0;
    }

    问题 G 套马的汉子

    求n矩阵 对于其中某个位置 在每个矩阵中都是横纵的最小点 就标记

    问有几个标记

    QWQ:

    看到第一反应二维线段树?

    看见前缀和可以直接做

    后来发现数据范围太小 就算直接写暴力枚举也能过

    做的时候不知道为什么题目又没看清写了最大 改回最小又浪费了一段时间

    写的时候 因为还是前缀和更好写就写了这个

    上下前后左右都枚举下用遍前缀和 标记下是否为最小

    统计答案

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    const int N=20,V=110;
    int n,x,y,mn,ans;
    int a[N][N][N],pd[N][N];
    int main()
    {
        n=read();x=read(),y=read();
        for(int i=1;i<=x;i++)
        for(int j=1;j<=y;j++)
          pd[i][j]=1;
        for(int k=1;k<=n;k++)
        for(int i=1;i<=x;i++)
        for(int j=1;j<=y;j++)
          a[k][i][j]=read();
        for(int k=1;k<=n;k++)
        {
            for(int i=1;i<=x;i++)
            {
                mn=1e9+7;
                for(int j=1;j<=y;j++)
                {
                    mn=min(mn,a[k][i][j]);
                    if(a[k][i][j]>mn) pd[i][j]=0;
                }
                mn=1e9+7;
                for(int j=y;j>=1;j--)
                {
                    mn=min(mn,a[k][i][j]);
                    if(a[k][i][j]>mn) pd[i][j]=0;
                }
            }
            for(int j=1;j<=y;j++)
            {
                mn=1e9+7;
                for(int i=1;i<=x;i++)
                {
                    mn=min(mn,a[k][i][j]);
                    if(a[k][i][j]>mn) pd[i][j]=0;
                }
                mn=1e9+7;
                for(int i=x;i>=1;i--)
                {
                    mn=min(mn,a[k][i][j]);
                    if(a[k][i][j]>mn) pd[i][j]=0;
                }
            }
        }
        
    //    for(int i=1;i<=x;i++,puts(""))
    //    for(int j=1;j<=y;j++) 
    //      cout<<pd[i][j]<<" ";
    //    
        for(int i=1;i<=x;i++)
        for(int j=1;j<=y;j++) if(pd[i][j])
          ans++;
        cout<<ans;
        return 0;
    }

    问题 H 繁忙的路口

    一个红绿灯入口

    给定一个状态 问是否矛盾

    QWQ:

    显然 对于每个入口来说是等价的

    强制对其中单一入口考虑

    分别考虑 当前路口直行有哪些矛盾 左行有哪些矛盾

       左行有5种矛盾 直行有4种矛盾(考场 刚开始纠结左行和右口的左行是否有矛盾 后来交了下确实是有矛盾的)

    再对于每个入口都这样做一遍判断有没有矛盾

    这里对于每个入口做一遍的时候推荐用循环数组枚举每个入口 

    具体实现 请参考代码   0表示左行 1表示直行

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    char s[20];
    inline int del(int x)
    {
        x=(x+24)%4;
        if(x==0) x+=4;
        return x;
    }
    inline int dw(int x,int y)
    {
        return (x-1)*2+y+1;
    }
    int main()
    {
        scanf("%s",s+1);
        for(int x=1;x<=4;x++)
        {
            if(s[dw(x,0)]=='G')
            {
                if(s[dw(del(x+1),0)]=='G'){cout<<"terrible";return 0;}
                if(s[dw(del(x+3),1)]=='G'){cout<<"terrible";return 0;}
                if(s[dw(del(x+3),0)]=='G'){cout<<"terrible";return 0;}
                if(s[dw(del(x+2),1)]=='G'){cout<<"terrible";return 0;}
                if(s[dw(del(x+2),0)]=='G'){cout<<"terrible";return 0;}
            }
            if(s[dw(x,1)]=='G')
            {
                if(s[dw(del(x+1),0)]=='G'){cout<<"terrible";return 0;}
                if(s[dw(del(x+1),0)]=='G'){cout<<"terrible";return 0;}
                if(s[dw(del(x+2),0)]=='G'){cout<<"terrible";return 0;}
                if(s[dw(del(x+3),1)]=='G'){cout<<"terrible";return 0;}
            }
        }
        cout<<"perfect";
        return 0;
    }

    问题 I roll up

    给你120个菜 6个人

    拿最多的人和最小的人都被淘汰

    第一个人拿的量已给出

    后每个人 先保证 不被淘汰 再保证淘汰的人最多 再保证自己拿的菜尽可能多

    菜不必取完

    QWQ:

    因为第一个人的量a给出    第二个人就比较特殊 知道前面的人的值

    考虑对a分类讨论

    1   a>58                  保证a肯定最大 得出a>58            第二个人尽量取 其他人全1

    2   a==58                结果 a-1   2   1  1

    3  (20<a&&a<58)    因为平分每个人20     该范围此时保证 a非最小  后面人尽量取a-1为非最大    保证最后个人取最小 

    4   a<=20 

        考场的时候这边不确定 考虑到答案可能有2种情况     因为菜不必取完 第二个人无论取多少 都不能保证不被淘汰

         1  因为 第二人不能保证不被淘汰  所以保证可能性尽可能大 淘汰尽可能多 取a 

             后几个人也这么做 最后所有人都取a 所有人一起淘汰

             按这样的推理 到最后个人 最后的人发现前面人取总肯定不会 和 前面三种的任意一种相同   

             那么 得知 a取值情况必为第四种     那么按推理 前面人都是a   

             最后个人按取的优先级必定把剩下的取完 淘汰的还是6个人

             而第二的人想到前面的情况 也没法改变取值保证 淘汰人数6不变 取值变多 

             交上去发现 这种方法错了  ps:后来出题人说了应该删去淘汰人尽可能多的条件

         2  因为 第二人不能保证不被淘汰  在第二人取尽可能多时保证淘汰人数5人 并且取值最大

             交上去发现从从50到71分了   发现std是这种情况 

    考场上不知道为什么情况3的58打成28 又搞来搞查了半天 以为其他几个直接输出的结果有问题

    最后每种情况都单独交了下 才查出来

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    const int N=120;
    int a;
    int b[N];
    int main()
    {
        a=read();
        if(a>58) cout<<N-a-4<<" "<<1<<" "<<1<<" "<<1<<" "<<1; //33
        if(a==58) cout<<a-1<<" "<<2<<" "<<1<<" "<<1<<" "<<1; 
        if(20<a&&a<58)//20-28 17 38
        {
            int sum=a;
            for(int i=1;i<=5;i++)
            {
                b[i]=min(N-sum-(5-i),a-1);
                sum+=b[i];
            }
            cout<<b[1];
            for(int i=2;i<=5;i++) cout<<" "<<b[i];
        } 
    //    if(a==20) cout<<20<<" "<<20<<" "<<20<<" "<<20<<" "<<20; 
    //    if(a<20) cout<<a<<" "<<a<<" "<<a<<" "<<a<<" "<<a; 
        if(a<=20) cout<<N-a-4<<" "<<1<<" "<<1<<" "<<1<<" "<<1; //21
        return 0;
    }

     问题 J 羊村快递站

    给个数列  每次按给定的位置取 最后输出取的次序 没取为0 被删掉为-1

    如果对与一个还未取的位置如 果右边先被取了 左边再被取了 那么该位置被删掉

    QWQ:

    考虑按取的次序一个个处理

    用个指针r 标记到当前情况最有边已经被取的位置

    用树状数组处理被删的情况

    对于当前取的位置w

    1   r<=w    更新指针 更新答案

    2   w<r  

                     如果没被删   更新答案 用区间-1  更新w+1到r-1值

                     被删了跳过 或者更新答案

    最后判断哪些位置没更新答案 并且值小于0 表示被删掉了 更新答案

    考场 没找到自己树状数组模板 太久没写了 以防万一

    随便找了板子 码风有点奇怪(考后改掉了)

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    const int N=1e4+10;
    int n,m,l,r,topa;
    int a[N],b[N],mp[N],tr[N],ans[N];
    inline void add(int x,int v)
    {
        for(;x<=n;x+=x&(-x)) tr[x]+=v;
    }
    inline int ask(int x)
    {
        int ans=0;
        for(;x;x-=x&(-x)) ans+=tr[x];
        return ans; 
    }
    int main()
    {
        n=read();r=0;l=n+1;
        for(int i=1;i<=n;i++) a[i]=read(),mp[a[i]]=i;
        m=read();
        for(int i=1;i<=m;i++) b[i]=read();
        for(int i=1;i<=m;i++)
        {
            int w=mp[b[i]];
            if(r<=w){r=w,ans[w]=++topa;continue;}//l=w-1;
            if(w<r)
            {
                if(ask(w)>=0)
                {
                    ans[w]=++topa;
                    add(w+1,-1);add(r,1);
                    continue;
                }
            }
        }
        for(int i=1;i<=n;i++) if(!ans[i]) if(ask(i)<0) ans[i]=-1;
        for(int i=1;i<=n;i++) printf("%d ",ans[i]);
        return 0;
    }

    问题 K 推箱子

    给你个推箱子的地图 问最小步数 最小方案数 最小步数的最小字典序的方案

    QWQ:

    代码在出题人的题解里放了

    考场A掉的大佬tql

    考场一看是到大模拟 果断跳

    问题 L Alice and Bob

    给你n个有编号的石子 每次只能取与编号与当前最大编号互质的石子 每次最多取n个

    两个人取 问谁赢

    QWQ:

    emmmm 怎么说呢 原题的  只有唯一公约数 怎么看怎么奇怪

    考场竟然一直没意识到就是 互质  

    知道意思后发现 最大的石子 除了n==1 其他情况是永远取不到的

    那么能取的石子是固定

    原题就转化为

     1 给你一个数n 求小于等于n的与n互质的数x

     2 给你个x个数的石子 每次限取m个 问谁赢

    说来惭愧    考后写的时候  已经是完全忘记了欧拉函数的定义就是 问1 了      数论学得太不扎实了

    线性筛oula函数就解决了问1

    问2就是经典的取石子问题 

        1   x%(m+1)==0   那么无论先手取什么 后手总能 取到一个数 使此轮 两人加起来取到m+1 最后 后手胜

        2   x%(m+1)!=0    那么先手可以肯定可以一定数   使得剩下来的数被(m+1)整除 转化为情况1     先手胜

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    ll n,m; 
    const ll N=1e7+10;
    int vis[N],prime[N],top,oula[N];
    void xxoula(ll mx)
    {
        vis[0]=vis[1]=1;//1为不是0为是 
        oula[0]=0;oula[1]=1;
        for(ll i=2;i<=mx;i++)
        {
            if(!vis[i]){prime[++top]=i;oula[i]=i-1;}
            for(ll j=1;j<=top&&prime[j]*i<=mx;j++)
            {
                vis[prime[j]*i]=1;
                if(!(i%prime[j])){oula[prime[j]*i]=oula[i]*prime[j];break;}
                oula[prime[j]*i]=oula[i]*(prime[j]-1);
            }
        }
    } 
    int main()
    {
        xxoula(1e7);
        ll t=read();
        while(t--)
        {
            n=read(),m=read();
            if((oula[n])%(m+1)) printf("Alice\n");
            else printf("Bob\n");
        }
        return 0;
    }

    问题 M 简单字符串匹配

    给你两个串a,b   问b移位后和对a的最大贡献

    ABCD组成一个环的顺序 后项对前一项 算1的贡献

    QWQ:

    感觉这场考试收获最大的就是这题了 重新理解了fft的优化

    考虑对a预处理 直接把每个位置的值按ABCD循环加1变换

    题目就转换成  b移位后和对a的最大贡献 位置上值相等算1贡献

    接着就感觉特别熟悉

    看到题目反应套用kmp的思想 处理信息

    然后就发现这样的话信息极难维护

    然后就想到了bitset

    对ABCD各单独考虑贡献

    该位置有  bitset中该值就为1

    统计贡献就是a b的bitset 取&

    太久没用 完全忘记了一次复杂度是n/32级别的 都是拿来卡常用的

    考场以为复杂度是lgn级别 写完后喜获暴力的分数33分 复杂度 O(nn/32)

    考场后看到了某个大佬发的链接 说用fft优化 

    大佬的代码太长了懒得仔细看了就自己写了

    考虑原来复杂度的瓶颈是统计贡献n*n/32

    对于 单一的一个值   假设m=6

    统计贡献的情况是

    相当上下两数列各位相乘

    而对与多项式卷积来说

    x+6+x+1维值的计算 相当于下图

     那么将b数组前后翻转下就通过卷积就可以得到   该情况的贡献了

    移动各个长度答案也就是卷积后的各个维度

    用fft优化卷积  对于 单一的一个值 移动各个长度的的贡献就能求出来 复杂度nlgn

    4个值总复杂度 O(4*nlgn)

    注意每次fft的时候 初始化卷积的ab数组到记得初始到最小的2^x>n+m

    另外题目中   m+移动的长度  是允许大于n的(允许两边都能空的赛马真的太奇怪了)

    关于之前的暴力用的stl 如果stl用int实现的话 手写个ll的 除/64的常数 

    因为答案只要最优的一个移动位置就能计算了 缩小下移动枚举的量

    用+rand()枚举移动位置  数据水点 机器快点 多交几遍运气好点说不定能过 

    bitset代码

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    const int N=1e5+10;
    int n,m,ans;
    char a[N],b[N];
    bitset<N> bt1[6],bt2[6],bt3[6];
    inline int del(int x)
    {
        x=x%4;
        if(x==0) x=4; 
        return x;
    }
    int main()
    {
        n=read(),m=read();
        scanf("%s",a+1);scanf("%s",b+1);
        for(int i=1;i<=n;i++) bt1[del(a[i]-'A'+2)][i]=1;
        for(int i=1;i<=m;i++) bt2[del(b[i]-'A'+1)][i]=1;
        for(int i=1;i<=n;i++)
        {
            int tmp=0;
            for(int k=1;k<=4;k++)
            {
                bt3[k]=bt1[k]&(bt2[k]<<i);
                tmp+=bt3[k].count();
            }
            ans=max(ans,tmp);
        }
        cout<<ans;
    }

    fft代码

    #include<bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define C getchar()-48
    inline ll read()
    {
        ll s=0,r=1;
        char c=C;
        for(;c<0||c>9;c=C) if(c==-3) r=-1;
        for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
        return s*r;
    } 
    const int N=3e5+10;
    int nn,mm,ans;
    char aa[N],bb[N];
    int bt1[6][N],bt2[6][N],bt3[6][N];
    inline int del(int x)
    {
        x=x%4;
        if(x==0) x=4; 
        return x;
    }
    #define E complex<double> 
    //N记得至少开两倍 
    const double pi=acos(-1);
    int n,m,l,r[N];
    E a[N],b[N];
    void fft(E *a,int f){
        for(int i=0;i<n;i++)if(i<r[i])swap(a[i],a[r[i]]);//交换位置  if为了避免重复交换变回原来的 
        for(int i=1;i<n;i<<=1){//当前合并两个长度为i的值的集合 
            E wn(cos(pi/i),f*sin(pi/i));//单位复根 将一个圆分成i部分 因为每次要合并i对下标为奇数和欧素的 
            for(int p=i<<1,j=0;j<n;j+=p){//当前要合并区间的第一个位置p   
                E w(1,0);
                for(int k=0;k<i;k++,w*=wn){//要合并这个区间的第几个数 
                    E x=a[j+k],y=w*a[j+k+i];
                    a[j+k]=x+y;a[j+k+i]=x-y;  //算出带进去的两个值的结果 
                }
            }
        }
    }
    inline void cl()
    {
        n=nn,m=mm;l=0;
        for(int i=0;i<=N-10;i++) r[i]=0;
        for(int i=0;i<=N-10;i++) a[i]=b[i]=0;
    }
    int main()
    {
        nn=read(),mm=read();
        scanf("%s",aa+1);scanf("%s",bb+1);
        for(int i=1;i<=nn;i++) bt1[del(aa[i]-'A'+2)][i]=1;
        for(int i=1;i<=mm;i++) bt2[del(bb[i]-'A'+1)][i]=1;
        for(int k=1;k<=4;k++)//
        {
               cl(); 
            m+=n;for(n=1;n<=m;n<<=1)l++;//乘运算后的长度至少为n+m  运算要求为2的整次幂
            for(int i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));//蝴蝶操作 
            for(int i=1;i<=nn;i++) a[i]=bt1[k][i];
            for(int i=1;i<=mm;i++) b[i]=bt2[k][mm-i+1];;
            fft(a,1);fft(b,1);//转化为点值表达 
            for(int i=0;i<=n;i++)a[i]=a[i]*b[i];//O(n)运算 
            fft(a,-1);//转化为系数表达 
            for(int i=0;i<=m;i++) bt3[k][i]=(int)(a[i].real()/n+0.5);;
        }    
        for(int i=0;i<=nn;i++)
        {
            int tmp=0;
            for(int k=1;k<=4;k++) tmp+=bt3[k][1+mm+i];
            ans=max(ans,tmp);
        }
        cout<<ans;
    }
  • 相关阅读:
    汇编10:CALL和RET指令
    汇编09:转移指令的原理
    汇编08:数据处理的两个基本问题
    汇编07:定位内存地址的方法
    汇编06:包含多个段的程序
    汇编05:[BX]和loop指令
    汇编04:第一个汇编程序
    汇编03:寄存器
    C#版的mongodb最新的官方驱动2.4.0版本
    如何教你看懂复杂的正则表达式
  • 原文地址:https://www.cnblogs.com/1436177712qqcom/p/15677755.html
Copyright © 2011-2022 走看看