CSP-J模拟赛(5)补题报告

前言:

           1.牛奶(milk):100

           2.树组(Traary):10

           3,智乃的兔子(usagi):0

           4.一颗成熟的奥术飞弹(missiles):0

       总结一下,今天的题比正式考试难。没有200是因为第2题没想到O(N)的解法+实现有错误,3,4题没法骗分。复赛时要是AC前两题,再加上后两题能骗到分,是能拿一奖的(比较极限)。如果让我评价今天的考试的话,那就是:

 (这破题我不做也罢)

牛奶

题意:

冰箱里有 n个种类的牛奶,它们有各自的数量 ai 和价格 bi​​。作为一只学过动态规划的猫,Meowowco 一个月需要 m 盒牛奶,她想知道屯够一个月的牛奶量的最小开销。

考试回顾:

一开始想到背包(但我不会实现),研究一下发现O(N)就能过。其实不算特别难,主要是题目唬人。

题解:

数组+排序,每次累加最小的单价(注意开long long)。

AC: 
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m;
long long cnt,ans;//cnt计算价格,ans计算数量 
struct node{
	int sl,dj;
}a[N]; 
bool cmp(node x,node y){
	return x.dj<y.dj;
}
int main(){
	//freopen("milk.in","r",stdin);
	//freopen("milk.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&a[i].sl,&a[i].dj);
	}
   sort(a+1,a+n+1,cmp);
   for(int i=1;i<=n;i++){
   	    if(ans>=m){
   	       break;
		}
		else{
			if(m-ans<a[i].sl){
				cnt+=a[i].dj*(m-ans);
				ans+=(m-ans);
				break;
			}
			else if(m-ans>=a[i].sl){
				cnt+=a[i].dj*a[i].sl;
				ans+=a[i].sl;

			}
		}
   }  
    printf("%lld",cnt);
	return 0;
}

树组

题意:

Meowowco 有 n 棵树苗,今天要在数组的每一个位置种(物理)上一棵树。种好之后,我们称它为树组。

最开始,树组中所有的树的高度为 0。每天过后,每棵树会自然生长 1 单位高度。

Meowowco 的种树过程持续 m 天,在每一天早上,她有三种操作:

op=1:选择某棵树 x对其施展魔法,该效果持续 k 天(包括当天)。拥有魔法效果的树每天晚上会额外生长 1 单位高度。若施展时该树已经存在魔法效果,则覆盖原来的魔法效果(也就是取消原来的魔法效果,加上这次的魔法效果)。

op=2:选择取消某棵树 x 的魔法效果,可能会对没有施加魔法的树进行操作。

op=3:Meowowko 想知道该天某棵树 x 的高度。

对于每个 op=3,输出一个整数 h,代表该树的高度。

考试回顾:

读题花了一些时间,想到了O(N^2)的大模拟,优化到了10^8左右。但正解是线性的O(N),在实现上 也有问题,没有AC。

题解:

day记录天数,对于每棵树的魔法进行分类讨论:(1)没有魔法(2)施过但是失效(3)还没施完

用f数组存起来,遇到op==3直接输出。可以做到O(1)单个操作维护。

AC: 
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,k,day,f[N],g[N],h[N];
void over(int x){
	if(g[x]==0) return ;
	if(f[x]+g[x]-1<=day){// 魔法结束天数小于等于day;
		h[x]+=g[x];//完整地多生长了g[x];
		g[x]=0;
	}
	else{
		int tmp=f[x]+g[x]-1-day;
		h[x]+=day-f[x]+1;
		g[x]=tmp;
		f[x]=day+1;
	}
}
int main(){
	//freopen("traary.in","r",stdin);
    //freopen("traary.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    while(m--){//day:已经生长了的天数  g[x]:魔法的持续时间 
    	int op,x;
    	scanf("%d%d",&op,&x);
    	if(op==1){
    		over(x);
    		f[x]=day+1;
    		g[x]=k;
		}
		else if(op==2){
			over(x);
			g[x]=0;
		}
		else{
			over(x);
			printf("%d\n",h[x]+day);
		}
		day++;
	}
	return 0;
}

智乃的兔子

题意:

今天 Chino 在梦境世界中被可爱的兔子环绕,它们都是这个梦境世界的卡密——Cocoa 的使徒。每一只棉花糖般的兔子都有一个可爱值a​i​​。

“超想和可爱的小兔子们贴贴 ∼”

因此她向 Cocoa 许愿:请让我挑选出一些可爱的兔子。

但是,Cocoa 并不希望 Chino 随意挑选兔子,她希望 Chino 挑选出的兔子的可爱值的和是 77 的倍数。Cocoa 作为这个世界的卡密,觉得仅有这一条挑选规则会让游戏变得很无趣,她制定规则的目的,很可能是,吃掉 Chino…!?于是她又增加了一条规则:

Cocoa 亲吻了所有的兔子,它们都受到了”祝福”,当 Chino 选择这只兔子之后,她将会获得祝福值 bib​i​​。当 Chino 拥有的祝福值超过 H点时,会被 Cocoa 吃掉。

Chino 想知道怎么样才能完成挑选可爱值和为 7 的倍数的兔子,可爱和最大的同时还不会被吃掉。

考试回顾:

尝试O(N*N*M)暴力骗分, TL意料之中。

题解:

分类讨论,当h极大时忽略祝福值,使用滚动数组优化二维费用背包;当h正常时,二维费用背包变三维费用背包(f (i,j,k)意为第i个兔子的祝福值不超过j,它%7的数是k),也用滚动数组加位运算进行优化。

AC: 
#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int N=1e5+5;
int n,h,a[10005],b[10005];
ll f[2][7],dp[2][1005][7];
int main(){
	//freopen("usagi.in","r",stdin);
    //freopen("usagi.out","w",stdout);
   scanf("%d%d",&n,&h);
   for(int i=1;i<=n;i++) scanf("%d",&a[i]);
   for(int i=1;i<=n;i++) scanf("%d",&b[i]);
   if(h==998244353){//极限值情况下,祝福值无用
       memset(f,-0x3f,sizeof(f));
       f[0][0]=0;
       for(int i=1;i<=n;i++){//滚动数组优化二位费用背包
           int t=i&1;//用奇偶性来判断它在“这一层”,还是“上一层”
           for(int j=0;j<7;j++){
               f[t][j]=max(f[t^1][j],f[t^1][(((j-a[i])%7+7)%7)]+a[i]);
           }
       }
       printf("%lld\n",f[n&1][0]);
       return 0;
   }
   memset(dp,-0x3f,sizeof(dp));
   dp[0][0][0]=0;
   for(int i=1;i<=n;i++){//滚动数组优化三维费用背包
       int t=i&1;
       for(int j=0;j<=h;j++){
           for(int k=0;k<7;k++){
               if(j<b[i])  dp[t][j][k]=dp[t^1][j][k];
               else dp[t][j][k]=max(dp[t^1][j][k],
               dp[t^1][j-b[i]][(((k-a[i])%7)+7)%7]+a[i]);//状态转移方程
           }
       }
   }
   ll ans=0;
   for(int i=0;i<=h;i++){
       ans=max(ans,dp[n&1][i][0]);
   }
   printf("%lld",ans);
	return 0;
}

一颗成熟的奥术飞弹

题意:

     n 个点的无向连通图,无重边和自环,目标是从 1 点到达 n 点。在一个点上,如果有多条最短路径到达终点,则 可能偏离数 加一,求出最短路径的总条数,以及所有最短路中可能偏离数的最大值(对 998244353取模)。

考试回顾:

使用bfs暴搜+加计数,时间超限没有悬念(怎么3,4都时超了呢,是我解法太暴力?)。

题解:

    从n至1进行宽搜(用数组模拟队列),得到最短路径以及每个点的下一步(因为倒着搜,每个点的后继就是它的前驱)再遍历存好的数组,判断一个点是否在另一个点的最短路径上,可以得出结论:从 x到 y的边在最短路可以看作从x 的最短距离等于 y的最短距离 +1。最终要求从 到 的最短路计数,可以离 从近到远的考虑,枚举(x , y) ,如果dis x=dis y+1,说明(x , y) 在最短路边上,情况进行累加,cnt x += cnt y。

AC: 
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,mod=998244353;
struct node{
	int to,nxt;
}edge[N<<2];
int n,m,cnt=1,head[N],hd=1,tail,q[N],dis[N],pre[N],tot[N],ans[N];
void add(int u,int v){
	edge[cnt].to=v;
	edge[cnt].nxt=head[u];
	head[u]=cnt++;
}
void bfs(){//数组模拟队列,从n搜到1。 
	memset(dis,-1,sizeof(dis));
	dis[n]=0;
	q[++tail]=n;
	while(hd<=tail){
		int u=q[hd++];
		for(int i=head[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(dis[v]==-1){
				dis[v]=dis[u]+1;
				pre[v]=u;
				q[++tail]=v;
			}
		}
	}
}
int main(){ 
	//freopen("missiles.in","r",stdin);
    //freopen("missiles.out","w",stdout);
    scanf("%d%d",&n,&m);
    while(m--){
    	int u,v;
    	scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
	}
	bfs();
	tot[n]=1;
	for(int i=1;i<=tail;i++){
		int u=q[i],flag=0;
		for(int j=head[u];j;j=edge[j].nxt){
			int v=edge[j].to;
			if(dis[u]==dis[v]+1){
				ans[u]=max(ans[u],ans[v]);
				(tot[u]+=tot[v])%=mod;
				if(v!=pre[u]) flag=1;
			}
		}
		ans[u]+=flag;
	}
	printf("%d %d\n",tot[1],ans[1]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值