开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第31天,点击查看活动详情
【牛客】牛客小白月赛64 A-F 题解
A 小杜要迟到了!
题目大意
小杜现在在 楼,上课地点在 楼。已知小杜走楼梯速度为 秒每层楼,电梯运行速度为 秒每层楼。然而最开始电梯停留在 楼,也就是说,电梯要先运行至 楼才能接上小杜。
假设此时没有他人等电梯,且忽略电梯上下客的时间,且除了1楼外其他楼层不能呼叫电梯。
小杜想知道是走楼梯快还是坐电梯快还是一样快。
思路
步行上楼需要爬 层,用时为 。
坐电梯除了需要上升 层外,还要等电梯下降 层,用时为 ,
比较即可。
代码
#include <stdio.h>
int main()
{
int n,k,a,b;
scanf("%d%d%d%d",&n,&k,&a,&b);
int tw=(n-1)*a;
int te=(k-1)*b+(n-1)*b;
if (te<tw) printf("1");
else if (te>tw) printf("2");
else printf("0");
return 0;
}
B 小杜捕鱼
题目大意
的池塘里有若干条鱼,用二维矩阵描述,每个格点是 .
表示该点没有鱼,是 #
表示该点有鱼。
在一个点撒网,能够网住所有距离该点曼哈顿距离小于 的鱼。求最小的 值,使得不论在哪个点撒网都可以网住池塘里所有鱼。
思路
反过来想, 其实是每个鱼到所有点的最远曼哈顿距离。
显然每个鱼到整个池塘四个角之一的曼哈顿距离最远,所以答案就是输出每个鱼到四个角距离的最大值。
代码
#include <stdio.h>
int main()
{
int n,m,k=0;
char ch;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
{
scanf(" %c",&ch);
if (ch=='#')
{
if (i-1+j-1>k) k=i-1+j-1;
if (n-i+j-1>k) k=n-i+j-1;
if (i-1+m-j>k) k=i-1+m-j;
if (n-i+m-j>k) k=n-i+m-j;
}
}
printf("%d\n",k);
}
C Karashi的生日蛋糕
题目大意
蛋糕上水果摆放成 圈,且第 圈必须有 个水果。
把蛋糕均分成 块。要求如下:
- 每块蛋糕包含第 圈的水果数量为 或 个。
- 并且任意两块蛋糕包含的水果总个数相差不得超过 1。
试构建一种水果的摆放方案,输出 行 列的非负整数矩阵,第 行第 列的数字 表示第 块蛋糕上有 个第 种水果。
思路
先把 个水果平均分配给 块蛋糕,即每块蛋糕先分 个第 种水果,然后把余数循环分配给每一个人即可。
代码
#include <stdio.h>
#include <algorithm>
#include <vector>
using namespace std;
const int N=1000001;
int n,k;
vector<int> a[N];
int frt[N];
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=k;++i)
{
a[i].push_back(0);
for (int j=1;j<=n;++j) a[i].push_back(j/k);
}
int t=1;
for (int i=1;i<=n;++i)
{
for (int j=1;j<=i%k;++j)
{
a[t][i]++;
t=t%k+1;
}
}
for (int i=1;i<=k;++i,printf("\n"))
for (int j=1;j<=n;++j) printf("%d ",a[i][j]);
return 0;
}
D Karashi的树 I
题目大意
个点以节点 1 为根的树每个点都有点权 ,每个点 的收益是它到根的的路径上所有节点的点权和。我们可以执行若干次下述操作:
- 选择一个节点,使得它到根节点路径上的点权全部翻转,如下图所示:
执行若干次操作,最大化所有点的收益之和。
思路
容易猜到任意节点的权值都能变成其他的,所以建后统计每个节点子树的大小记为 。
假设节点 的权值为 ,且以节点 为根的子树的大小为 ,那么就有 个节点的根路径上有节点 ,则 对答案的贡献为 。
所以我们肯定希望权值大的节点有更多的子节点。所以答案是把 和权值 分别排序后对应相乘再相加即可。
"任意节点的权值都能变成其他的,"
刚才我们说这个结论是猜到的。那么怎么证明呢?比如一条链是:1-2-3-4-5
我们可以将其翻转变成:5-4-3-2-1
然后翻转前两个节点变成:4-5-3-2-1
然后再做一次整体翻转:1-2-3-4-5这就说明了整棵树中任意两个相邻的节点可以交换,且不破坏除了这两个节点之外树其他部分的结构。所以整棵树上任意节点都可以取到任意值。
代码
#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;
const int N=300001;
int n,a[N],siz[N];
vector<int> e[N];
void dfs(int u,int fa)
{
siz[u]=1;
for (auto v:e[u])
{
if (v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
sort(a+1,a+1+n);
for (int x,i=2;i<=n;++i)
{
scanf("%d",&x);
e[x].push_back(i);
}
dfs(1,0);
sort(siz+1,siz+1+n);
long long ans=0;
for (int i=1;i<=n;++i) ans+=1ll*siz[i]*a[i];
printf("%lld",ans);
return 0;
}
E Karashi的数组 I
题目
思路
令 ,则式子
就可以转化为
所以 和 至少有一个是 时, 对答案有贡献。
我们只需要先求一遍符合题意的 的数量,每次修改将 的值改为 ,只会对两个区间产生影响,相应修改答案即可。
代码
#include <stdio.h>
const int N=500001;
int n,m,len,a[N];
int main()
{
scanf("%d%d%d",&n,&m,&len);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
int cnt=0;
for (int i=1;i+len+1<=n;++i)
if (a[i]*a[i+len+1]==0) cnt++;
for (int i=1,x,y;i<=m;++i)
{
scanf("%d%d",&x,&y);
if (x+len+1<=n&&a[x]==0&&a[x+len+1]!=0) cnt--;
if (x-len-1>=1&&a[x]==0&&a[x-len-1]!=0) cnt--;
if (x+len+1<=n&&y==0&&a[x+len+1]!=0) cnt++;
if (x-len-1>=1&&y==0&&a[x-len-1]!=0) cnt++;
a[x]=y;
printf("%d\n",cnt);
}
return 0;
}
F 小杜跑酷
题目
思路
容易想到 很小的话,我们可以直接给陷阱打上标记,然后遍历前 列,设 表示走到第 行第 列的方案数。很显然的转移如下:
- 如果第 行第 列有机关,则
- 否则
虽然 很大,但是如果第 列没有机关,那么到第 列的方案数将不需要经过上述转移。所以我们直接对所有输入的机关按列号排序进行 DP 即可。
代码
#include <stdio.h>
#include <algorithm>
using namespace std;
using LL=long long;
const LL mod=998244353;
const int N=500005;
struct asdf{
int x,y;
bool operator < (const asdf a) const
{
return y<a.y;
}
}a[N];
int n,m,v[4];
LL f[4]={0,1,0,0};
LL g[4],h[4];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i) scanf("%d%d",&a[i].x,&a[i].y);
sort(a+1,a+1+m);
for (int i=1;i<=m;++i)
{
v[a[i].x]=1;
if (i<m&&a[i+1].y==a[i].y) continue;
for (int j=1;j<=3;++j)
if (v[j])
{
if (a[i].y!=n-1) h[j]=(h[j]+f[j])%mod;
else g[j]=(g[j]+f[j])%mod;
if (j-1>0) g[j-1]=(g[j-1]+f[j])%mod;
else g[j]=(g[j]+f[j])%mod;
if (j+1<4) g[j+1]=(g[j+1]+f[j])%mod;
else g[j]=(g[j]+f[j])%mod;
}
else g[j]=(g[j]+f[j])%mod;
for (int j=1;j<=3;++j)
{
f[j]=g[j];
g[j]=h[j];
h[j]=v[j]=0;
}
if (a[i+1].y!=a[i].y+1)
for (int j=1;j<=3;++j) f[j]+=g[j],g[j]=0;
}
for (int j=1;j<=3;++j) printf("%lld\n",(f[j]+g[j])%mod);
return 0;
}