2021JiangXi Provincinal Collegiate Programming Contest

2021 JiangXi Provincinal Collegiate Programing Contest

K. Many Littles Make a Mickle

  • 题意

​ 有n层楼,第i层楼有i*i个房间,每个房间可以容纳m个人,问n层楼总共可以容纳多少人

  • 题解

    水题

  • 代码

#include <iostream>

using namespace std;

int main() {
    int t;
    cin>>t;
    while(t--) {
        long long n,m;
        cin>>n>>m;
        
        long long res=0;
        for(int i=1;i<=n;i++) res+=i*i;
        res*=m;
        
        cout<<res<<'\n';
    }
    return 0;
}

B. Continued Fraction

  • 题意

    给一个分数x/y,把它化成一下形式,输出n,以及a[0~n]

  • 题解
    原式 = a 0 + 1 . . . a 0 = x y 1 . . . = y x   m o d   y . . . 作为新的原式继续计算 a 1 . . . a n 原式=a_0+\frac{1}{...}\\ a_0=\frac{x}{y}\\ \frac{1}{...}=\frac{y}{x\bmod y}\\ ...作为新的原式继续计算a_1...a_n 原式=a0+...1a0=yx...1=xmodyy...作为新的原式继续计算a1...an
    由上述的计算过程,可以发现每次都在用x%y和y来递归计算,和gcd一样,直接套用gcd,同时需要记录a数组大小及其值

  • 代码

#include <iostream>

using namespace std;
const int N=110;

int a[N],cnt;//分别记录a的值,数组a的大小

void gcd(int x,int y) {
    if(y==0) return ;
    a[cnt++]=x/y;
    gcd(y,x%y);
}

int main() {
    int t;
    cin>>t;
    while(t--) {
        int x,y;
        cin>>x>>y;
        
        cnt=0;//每次记得大小置为0
        gcd(x,y);
        
        cout<<cnt-1<<" ";//输出数组的最后下标n,所以-1
        for(int i=0;i<cnt;i++)cout<<a[i]<<" ";
        cout<<'\n';
    }
    return 0;
}

L. It Rains Again

  • 题意

    给定一些(x1,y2)到(x2,y2)的挡雨板,问最后不被淋到的地方有多少米

  • 题解

    可以看到纵坐标完全没用,直接简化成一位坐标就行,可以把题目抽象成这样一个模型:每给一个板子就把x1~x2区间内的加上一个板子,最后统计所有点开头的长度为1的区间不被淋的有几个;即对很多个个开区间[x1,x2)操作,然后看所有板数大于0点的总和,但直接对区间操作时间复杂度过高,可以转变为差分

  • 代码

#include <iostream>

using namespace std;
const int N=1e5+5;

int b[N];//差分数组
int res;

int main() {
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) {
        int x1,y1,x2,y2;
        cin>>x1>>y1>>x2>>y2;
        b[x1]++,b[x2]--;//开区间的差分操作
    }
    
    for(int i=1;i<=100000;i++) {
        b[i]+=b[i-1];//充当和数组
        if(b[i]>0) res++;
    }
    
    cout<<res<<'\n';
    
    return 0;
}

A. Mio visits ACGN Exhibition

  • 题意

​ 给n*m的只含有01的地图,问从(1,1)->(n,m)的不回头所有路径中至少经过p个0,q个1的路径数量

  • 题解

    一眼dp

    定义:如下,但是爆空间了
    f [ i , j , x , y ] : 从 ( 1 , 1 ) 到 ( i , j ) 所有路径有 x 个 0 , y 个 1 的数量 f[i,j,x,y]:从(1,1)到(i,j)所有路径有x个0,y个1的数量 f[i,j,x,y]:(1,1)(i,j)所有路径有x0y1的数量
    定义优化:从(1,1)到(n,m)步数固定,所以01个数和固定i+j-1==x+y
    f [ i , j , x ] : 从 ( 1 , 1 ) 到 ( i , j ) 所有路径有 x 个 0 , i + j − 1 − x 个 1 的数量 f[i,j,x]:从(1,1)到(i,j)所有路径有x个0,i+j-1-x个1的数量 f[i,j,x]:(1,1)(i,j)所有路径有x0i+j1x1的数量
    计算
    f [ i , j , x ] = { f [ i − 1 , j , x ] + f [ i , j − 1 , x ] g [ i , j ] = 1 f [ i − 1 , j , x − 1 ] + f [ i , j − 1 , x − 1 ] g [ i , j ] = 0 , x > 0 f[i,j,x]= \begin{cases} f[i-1,j,x]+f[i,j-1,x] &g[i,j]=1\\ f[i-1,j,x-1]+f[i,j-1,x-1] &g[i,j]=0,x>0 \end{cases} f[i,j,x]={f[i1,j,x]+f[i,j1,x]f[i1,j,x1]+f[i,j1,x1]g[i,j]=1g[i,j]=0,x>0
    虽然还是爆空间,但比可以发现只用到i和i-1这两层,所以可以用滚动数组优化

  1. 忘记初始化,以及初始化搞错定义
  2. 初始点需要continue,防止被覆盖
  3. k需要倒着遍历,脑子里想一下这个三维遍历图,会发现k=i+j-1时,f[j,k]一定是前一层没有遍历过得到的值,而f[j,k]需要用到上一层及上一行的旧的f值,所以从后往前遍历合理;如果从前往后遍历会出现这样的情况:所有的f[j,1]会由f[j,0]=0更新并且往后覆盖,导致所有f[j,k]=0
  4. g[i,j]=0&&k=0时,也需要更新值f[j,k]
  • 代码
#include <iostream>

using namespace std;
const int N=510,M=10010,mod=998244353;//题目要求取模

int n,m,p,q;
int g[N][N];
int f[N][M];

int main() {
    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&g[i][j]);
            
    f[1][!g[1][1]]=1;//按定义初始化
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) {
            if(i==1&&j==1)continue;//如果没有这个,初始化的值1会被覆盖为0
            for(int k=i+j-1;k>=0;k--)
                if(g[i][j]) f[j][k]=(f[j][k]+f[j-1][k])%mod;
                else {
                  	//可以转移的前提是k>0
                    if(k) f[j][k]=(f[j][k-1]+f[j-1][k-1])%mod;
                    else f[j][k]=0;//k=0时也需要更新,因为此时g[i,j]=0
                }									//说明路径上一定有一个0,故f[j,0]=0
        }
                
    int res=0;//把所有到了(n,m)的点且个数符合的所有路径数量和
    for(int i=p;i<=n+m-q-1;i++) res=(res+f[m][i])%mod;
    printf("%d\n",res);
    
    return 0;
}
  • 补充

    dp定义不变,但是集合划分即状态转移方程换一种方式

H. Hearthstone So Easy

  • 题意

    p与f玩一个游戏,游戏规则为:初始两人都有n点血,中毒值为0;游戏有四个阶段,p抽卡,p选择奶自己还是扣f血量k,f抽卡,f选择奶自己还是扣p血量k;抽卡阶段会扣中毒值血量,同时中毒值+1;当睡的血量<=0时游戏结束。问p,f谁赢

  • 题解(定性+定量)

    1. 可以发现,只要某一回合两人活着,那么结束时两人的血量一样

    2. 分析先手如何能取胜:

      • 先手劣势是一定会先在抽卡阶段受到伤害,相当于此时相对血量最少,可能会在这时被扣死
      • 所以先手需要尽量在上一轮没被抽卡扣死时把后手杀死
      • 故:先手要一直选择用卡杀后手
    3. 分析后手如何能取胜:

      • 后手不可能用卡杀死先手,因为如果后手能用卡,就说明后手在中毒+被先手扣的k血量不能致死,那么同样的如果后手用卡扣先手,对于先手也是中毒+被后手扣的k血量,并不能让先手死。
      • 所以后手只能维持一样血量的同时拖死先手,让先手被毒死
    4. 考虑先手在第m轮扣死后手,同时后手使用回奶维持同血量的
      { n − m ( m − 1 ) 2 − k − m > 0 , 先手要在 m − 1 轮被打 + m 轮抽卡不被杀 n − m ( m + 1 ) 2 − k < = 0 , 后手没来得及在 m 轮回奶维持原样就死 \begin{cases} n-\frac{m(m-1)}{2}-k-m>0 &,先手要在m-1轮被打+m轮抽卡不被杀\\ n-\frac{m(m+1)}{2}-k<=0 &,后手没来得及在m轮回奶维持原样就死\\ \end{cases} {n2m(m1)km>0n2m(m+1)k<=0,先手要在m1轮被打+m轮抽卡不被杀,后手没来得及在m轮回奶维持原样就死
      很显然上式矛盾,故先手除了在第一轮杀死后手就不可能必胜

  • 代码

#include <iostream>

using namespace std;

int main()
{
    int t;
    cin>>t;
    while(t--){
        int n,k;
        cin>>n>>k;
        //特判先手直接被扣死
        if(n==1){cout<<"freesin"<<'\n';continue;}
        
       //如果先手能在第一轮杀死后手,先手胜
        if(n<=k+1)cout<<"pllj"<<'\n';
       //否则后手必胜
        else cout<<"freesin"<<'\n';
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值