• 【洛谷】CYJian的水题大赛 解题报告


    点此进入比赛

    (T1):八百标兵奔北坡

    这应该是一道较水的送分题

    理论上来说,正解应该是DP但是,.前缀和优化暴力就能过

    放上我比赛时打的暴力代码吧((hl666)大佬说这种做法的均摊复杂度为(O(logn)),总复杂度应为(O(nlogn)),可以接受):

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define N 1000
    #define M 1000
    using namespace std;
    int n,m,Q,sum[N+5][M+5],a[N+5][M+5];
    inline char tc()
    {
        static char ff[100000],*A=ff,*B=ff;
        return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0;int f=1;char ch;
        while(!isdigit(ch=tc())) if(ch=='-') f=-1;
        while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
        x*=f;
    }
    inline void write(int x)
    {
        if(x<0) putchar('-'),x=-x;
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
    inline bool check(int x,int y1,int y2)//利用前缀和,判断出第x行y1~y2范围内是否有山
    { 
        return sum[x][min(y2,m)]-sum[x][max(y1-1,0)];
    }
    int main()
    {
        register int i,j;
        for(read(n),read(m),read(Q),i=1;i<=n;++i)
            for(j=1;j<=m;++j)
                read(a[i][j]);
        for(i=1;i<=n;++i)
            for(j=1;j<=m;sum[i][j]+=sum[i][j-1],++j)//统计每一行的前缀和
                if(a[i][j]>=max(max(a[i-1][j],a[i+1][j]),max(a[i][j-1],a[i][j+1]))),sum[i][j]=1;//若当前坐标有一坐山,则令sum[i][j]为1
        while(Q--)
        {
            int x,y;bool could=false;read(x),read(y);
            for(i=x;i;--i) 
                if(check(i,y-x+i,y+x-i))//判断是否可行
                {
                    write(x-i),putchar('
    '),could=true;//可以就输出答案,并标记有答案
                    break;
                }
            if(!could) puts("Pool Babingbaboom!");//若标记无答案,则输出"Pool Babingbaboom!"
        }
        return 0;
    }
    

    (T2):灰化肥,会挥发

    这道题一看到数据范围就会想到状压(DP)吧!(当然,也不乏有某些大佬会想到用模拟退火来做)

    设用(f[i][j])来表示在第(i)个谷仓,经过的谷仓集合为(j)时走过的最短路径,则$$f[i][j]=max(f[i][j],f[k][(j)xor (1<<(i-1))]+w[k][i])$$其中(w[k][i])表示(k)(i)的距离。

    我们可以BFS预处理出两两仓库间的距离,然后(DP)即可

    注:只可惜,我在比赛过程中不停爆0,比赛结束之后,请教(hl666)大佬才知道洛谷数据有(BUG)有字符串的题目不能打读优,结果白白爆(0),也算是一个教训吧!可怜我打了两个多小时的代码

    代码如下:

    #include<bits/stdc++.h>
    #define R 500
    #define C 500
    #define N 16
    using namespace std;
    int n,r,c,cnt=0,H,T,x[R*C+5],y[R*C+5],Step[R*C+5],To[30],w[N+5][N+5],vis[R+5][C+5]={0},f[N+5][(1<<N)+5];
    char ch[R+5][C+5];
    struct Warehouse//存储每个谷仓的信息
    {
        char Name;
        int x,y;
    }s[N+5];
    string S[N+5][(1<<N)+5];
    //不能打读优QwQ
    /*inline char tc()
    {
        static char ff[100000],*A=ff,*B=ff;
        return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0;int f=1;char ch;
        while(!isdigit(ch=tc())) if(ch=='-') f=-1;
        while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
        x*=f;
    }*/
    inline void write(int x)
    {
        if(x<0) putchar('-'),x=-x;
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
    inline void BFS(int Now)//BFS预处理
    {
        while(H<=T)
        {
            if(ch[x[H]][y[H]]>='A'&&ch[x[H]][y[H]]<='Z') w[Now][To[ch[x[H]][y[H]]-'A']]=Step[H];
            if(x[H]>1&&!vis[x[H]-1][y[H]]&&ch[x[H]-1][y[H]]!='*') vis[x[H]-1][y[H]]=1,x[++T]=x[H]-1,y[T]=y[H],Step[T]=Step[H]+1;//向上走
            if(x[H]<r&&!vis[x[H]+1][y[H]]&&ch[x[H]+1][y[H]]!='*') vis[x[H]+1][y[H]]=1,x[++T]=x[H]+1,y[T]=y[H],Step[T]=Step[H]+1;//向下走
            if(y[H]>1&&!vis[x[H]][y[H]-1]&&ch[x[H]][y[H]-1]!='*') vis[x[H]][y[H]-1]=1,x[++T]=x[H],y[T]=y[H]-1,Step[T]=Step[H]+1;//向左走
            if(y[H]<c&&!vis[x[H]][y[H]+1]&&ch[x[H]][y[H]+1]!='*') vis[x[H]][y[H]+1]=1,x[++T]=x[H],y[T]=y[H]+1,Step[T]=Step[H]+1;//向右走
            ++H;
        }
    }
    int main()
    {
        register int i,j,k;
        for(cin>>r>>c>>n,i=1;i<=r;++i)
            for(j=1;j<=c;++j)
            {
                cin>>ch[i][j];
                if(ch[i][j]>='A'&&ch[i][j]<='Z') s[To[ch[i][j]-'A']=++cnt]=(Warehouse){ch[i][j],i,j};//将每一个谷仓的位置与名称存储下来
            }
        for(i=1;i<=n;++i)//对每个谷仓进行预处理
        {
            memset(vis,0,sizeof(vis));
            vis[x[H=T=0]=s[i].x][y[0]=s[i].y]=1,Step[0]=0;
            BFS(i);
        }
        for(i=1;i<=n;++i) for(j=1;j<=(1<<n)-1;++j) f[i][j]=1e9;//DP预处理
        f[To[0]][1<<(To[0]-1)]=0,S[To[0]][1<<(To[0]-1)]="A";
        for(j=1;j<=(1<<n)-1;++j)
            for(i=1;i<=n;++i)
            {
                if(!(j&(1<<(i-1)))||!(j^(1<<(i-1)))) continue;
                for(k=1;k<=n;++k)
                    if((i^k)&&(j&(1<<(k-1)))&&((f[k][j^(1<<(i-1))]+w[k][i]<f[i][j])||(f[k][j^(1<<(i-1))]+w[k][i]==f[i][j]&&S[k][j^(1<<(i-1))]+(s[i].Name)<S[i][j]))) f[i][j]=f[k][j^(1<<(i-1))]+w[k][i],S[i][j]=S[k][j^(1<<(i-1))]+(s[i].Name);			
            }
        int Min=1e9;string ans;
        for(i=1;i<=n;++i) 
            if(f[i][(1<<n)-1]<Min||(f[i][(1<<n)-1]==Min&&S[i][(1<<n)-1]<ans)) Min=f[i][(1<<n)-1],ans=S[i][(1<<n)-1];//统计答案
        return write(Min),putchar('
    '),cout<<ans,0;
    }
    

    (T3):红鲤鱼与绿鲤鱼

    很明显,这是一道(fan)(ren)的数学题。((ykh)大佬(3min)切了此题)。

    读题,我们可以发现,无论红鲤鱼的出现时间如何变化,罚时始终是一样的。所以,我们只需要管绿鲤鱼出现的时间,因为只有这个才会影响答案。

    仔细考虑一下,我们可以推出下面这个式子:$$frac{5(2A+B+1)+5C_{B+A-1}^{B-1}(A+B+1)frac{A+B}{2}}{2}$$

    然后,即可暴力去求答案。代码如下:

    #include<bits/stdc++.h>
    #define ULL unsigned long long
    #define YKH 998244853
    using namespace std;
    ULL n,m;
    inline char tc()
    {
        static char ff[100000],*A=ff,*B=ff;
        return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(ULL &x)
    {
        x=0;ULL f=1;char ch;
        while(!isdigit(ch=tc())) if(ch=='-') f=-1;
        while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
        x*=f;
    }
    inline void write(ULL x)
    {
        if(x<0) putchar('-'),x=-x;
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
    inline ULL Inv(ULL x,ULL y)
    {
        ULL res=1;
        while(y)
        {
            if(y&1) (res*=x)%=YKH;
            (x*=x)%=YKH,y>>=1;
        }
        return res;
    }
    int main()
    {
        read(n),read(m),n%=YKH;
        register ULL i;ULL ans1=((n+m+1)%YKH*(n+m)%YKH)%YKH*(Inv(2,YKH-2)%YKH)%YKH,ans2=1,t=1;
        for(i=n+1;i<n+m;++i) (ans2*=i%YKH)%=YKH;
        for(i=2;i<m;++i) (ans2*=Inv(i%YKH,YKH-2))%=YKH;
        for(i=n+1;i<=n+m;++i) (t*=i%YKH)%=YKH;
        for(i=2;i<=m;++i) (t*=Inv(i%YKH,YKH-2))%=YKH;
        (ans2*=5ll)%=YKH;
        ULL ans=((ans1*ans2)%YKH*(Inv(t%YKH,YKH-2)%YKH))%YKH;	
        write((ans+5LL*(2LL*n%YKH+m+1)%YKH+YKH)%YKH);
        return 0;
    }
    

    但是,我们会发现,这个代码会(TLE),只能得(70)分。怎么办呢?这时就要用卡常大法
    终于,在我的不懈努力下, 我改到了(95)分。代码如下:

    #include<cstdio>
    #include<cctype>
    using namespace std;
    unsigned long long n,m;
    inline char tc()
    {
        static char ff[100000],*A=ff,*B=ff;
        return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(unsigned long long &x)
    {
        x=0;char ch;
        while(!isdigit(ch=tc()));
        while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(unsigned long long x)
    {
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
    inline unsigned long long Inv(unsigned long long x,unsigned long long y)
    {
        unsigned long long res=1;
        while(y)
        {
            if(y&1) (res*=x)%=998244853;
            (x*=x)%=998244853,y>>=1;
        }
        return res;
    }
    int main()
    {
        read(n),read(m),n%=998244853;
        register unsigned long long i;unsigned long long ans1=((n+m+1)%998244853*(n+m)%998244853)%998244853*(Inv(2,998244851)%998244853)%998244853,ans2=1,t=1,x;
        for(i=n+1;i<n+m;++i) (ans2*=i)%=998244853,(t*=i)%=998244853;
        for(i=2;i<m;++i) (ans2*=x=Inv(i,998244851))%=998244853,(t*=x)%=998244853;
        (t*=(n+m)*(Inv(m,998244851))%998244853)%=998244853,(ans2+=ans2<<2)%=998244853;
        unsigned long long ans=((ans1*ans2)%998244853*(Inv(t%998244853,998244851)%998244853))%998244853;	
        write((ans+(10*n%998244853+5*m+5)%998244853+998244853)%998244853);
        return 0;
    }
    

    此时,我才发现,貌似是我的方法本身有问题。(虽然(hl666)跟我用同样的方法,他却AC了,大概是因为我天生自带大常数吧!

    冷静一会儿,我们便可以发现,原式可以转化为$$5(2A+B+1)+5B(A+B+1)$$即$$5AB+5B^2+10A+10B+5$$这样就可以快速求出答案了。(可惜我比赛时没想到)

    代码如下:

    #include<cstdio>
    #include<cctype>
    using namespace std;
    unsigned long long n,m;
    inline char tc()
    {
        static char ff[100000],*A=ff,*B=ff;
        return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(unsigned long long &x)
    {
        x=0;char ch;
        while(!isdigit(ch=tc()));
        while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(unsigned long long x)
    {
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
    inline unsigned long long Inv(unsigned long long x,unsigned long long y)
    {
        unsigned long long res=1;
        while(y)
        {
            if(y&1) (res*=x)%=998244853;
            (x*=x)%=998244853,y>>=1;
        }
        return res;
    }
    int main()
    {
        read(n),read(m),n%=998244853;
        register unsigned long long i;
        unsigned long long ans=((n+m+1)*(n+m)%998244853*(Inv(2,998244851)%998244853)%998244853)*5%998244853;
        unsigned long long res=ans*m%998244853*Inv(n+m,998244851)%998244853;
        write((res+(10*n%998244853+5*m+5)%998244853+998244853)%998244853);
        return 0;
    }
    
  • 相关阅读:
    Qt 无边框窗体改变大小 完美实现
    深入Windows窗体原理及控件重绘技巧
    EF里Guid类型数据的自增长、时间戳和复杂类型的用法
    Entity Framework中的Identity map和Unit of Work模式
    使用SQLite数据库和Access数据库的一些经验总结
    实现Avl平衡树
    使用Ajax
    接口和类 反射的差异性
    Guacamole 介绍
    依赖注入(DI)和Ninject
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/LuoguContest8578.html
Copyright © 2020-2023  润新知