• 【洛谷】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;
    }
    
  • 相关阅读:
    adb常用命令
    视频WEB插件 V1.5.2 (海康威视)
    字体渐变 兼容 ie
    spring mvc问题:源服务器未能找到目标资源的表示或者是不愿公开一个已经存在的资源表示
    APIO 2022 游记
    Aspen丶Sun
    解决 Linux下ifconfig命令没有ens33信息
    Hadoop Writable和WritableComparable区别
    函数的二义性
    C++显式类型转换
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/LuoguContest8578.html
Copyright © 2020-2023  润新知