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

点此进入比赛


T 1 T1 T1:八百标兵奔北坡

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

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

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

#include
#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('\n'),could=true;//可以就输出答案,并标记有答案
                break;
            }
        if(!could) puts("Pool Babingbaboom!");//若标记无答案,则输出"Pool Babingbaboom!"
    }
    return 0;
}

T 2 T2 T2:灰化肥,会挥发

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

L i n k Link Link

状压 D P DP DP 详见博客 动态规划专题(一)——状压DP

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

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

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

代码如下:

#include
#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('\n'),cout<<ans,0;
}

T 3 T3 T3:红鲤鱼与绿鲤鱼

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

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

仔细考虑一下,我们可以推出下面这个式子: 5 ( 2 A + B + 1 ) + 5 C B + A − 1 B − 1 ∗ ( A + B + 1 ) ∗ A + B 2 2 \frac{5(2A+B+1)+5C_{B+A-1}^{B-1}*(A+B+1)*\frac{A+B}{2}}{2} 25(2A+B+1)+5CB+A1B1(A+B+1)2A+B

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

#include
#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;
}

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

#include
#include
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;
}

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

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

代码如下:

#include
#include
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;
}

你可能感兴趣的:(比赛,洛谷)