2.【LGR-148-Div.3】洛谷基础赛 #1 & MGOI Round I

【LGR-148-Div.3】洛谷基础赛 #1 & MGOI Round I

据说是普及组难度?

T1 P9502 『MGOI』Simple Round I | A. 魔法数字

『MGOI』Simple Round I | A. 魔法数字

100pts

题目描述

初级魔法士小 M 的魔法数字是 2

给定一个正整数 n,小 M 需要找到最大的 偶数 m,使得 2m<n

  • 又双叒叕是个水题,然后被又双叒叕水题爆切。。。。。。
  • 只要判断 log2(n)%2 是否为偶数,并且不能正好是 2n 次方,然后输出即可。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,i;
cin>>n;
if(int(log2(n))%2==0&&int(log2(n))!=log2(n))
cout<<(int)log2(n);
else if(int(log2(n))%2==1)
cout<<(int)log2(n)-1;
else
cout<<int(log2(n)-2);
}

T2 P9503 『MGOI』Simple Round I | B. 魔法照相馆

100pts

题目描述

小 M 正在准备入学所必需的魔法士证件,因此他来到了纵深巷的魔法照相馆。

在等待的时候,小 M 注意到魔法照相馆有三个幕布,颜色从左到右分别是红色、蓝色和白色。店主 zx 先生会根据客人的需求拉上或拉下这三个幕布,如下图所示:

幕布摆放在左边,按照红蓝白的顺序排列。人则坐在幕布右边,而 zx 先生则站在最右边给人拍照。幕布从右往左数,第一个没有拉上的幕布颜色将成为照片的背景颜色。

每次 zx 先生拉上或拉下一个幕布都需要消耗一个时间单位,而拍照不消耗时间。小 M 已经知道了他前面所有总共 n 个客人的需求。在初始状态为 全部拉下 的情况下,请你帮助小 M 计算他至少需要等待多少个时间单位。

  • 水题,只要模拟状态,再对号入座,就轻轻松松 AC 了。
  • (其中红色幕布不会拉上去)
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,i,sum=0,zt=111;
char q;
cin>>n;
for(i=1;i<=n;i++)
{
cin>>q;
//111 110 101 100
if(q=='W'&&(zt==101||zt==111))continue;
else if(q=='B'&&(zt==110))continue;
else if(q=='R'&&(zt==100))continue;
if(q=='W')
{
if(zt==110)zt=111,sum++;
else if(zt==100)zt=101,sum++;
}
if(q=='B')
{
if(zt==111||zt==100)zt=110,sum++;
else if(zt==101)zt=110,sum+=2;
}
if(q=='R')
{
if(zt==111)zt=100,sum+=2;
else if(zt==110||zt==101)zt=100,sum++;
}
}
cout<<sum;
}
  • 将各个幕布的状态枚举,拉下为 1 ,拉上为 0

T3P9504 『MGOI』Simple Round I | C. 魔法禁林

30pts

题目描述

开学的第一天,小 M 迫不及待地计划着前往神秘的禁林。

小 M 拥有两个重要的属性,魔力值和生命值。非常特别的是,初始时,这两个值可以由小 M 任意决定

禁林可以看作一张 n 个点 m 条边的无向简单连通图。小 M 将在禁林里面行走,从起点 s 走到 t

每经过一条边,小 M 的魔力值都会减去 1。同时,每条边上有一个具有攻击力属性的魔兽,小 M 要与之战斗。若小 M 经过这条边之前的魔力值为 k,这条边上魔兽的攻击力为 w,那么经过这条边时发生的战斗将会消耗 wk生命值。魔兽不会被打败,因此多次经过同一条边,每次都会发生战斗

小 M 需要保证,当他的魔力值消耗完时,他的生命值为 0,且此时走到 t 点。

你需要求出小 M 初始时需要的最小生命值。

【数据范围】

对于所有数据,1n200001m400001s,t,u,vnst,图为无向简单连通图,0w100

Subtask n m w 分值
1 5 10 10 11
2 2000 4000 10 27
3 20000 40000 1 19
4 20000 40000 100 43
  • 一开始觉得完全打不了,但是还是试了试。
  • 首先使用 dijkstra 求最短路,但是不知道走每条边的状态,后来想到倒推,以终点 t 为起点,推回起点 s
    30pts代码
#include<bits/stdc++.h>
using namespace std;
int n,m,s,t;
struct aa
{
int nxt,to,w;
}e[1100001];
int cnt=0,head[100001];
void add(int u,int v,int w)
{
e[++cnt]={head[u],v,w};
head[u]=cnt;
}
long double dis[100011];
int stp[100011];
int vis[100011];
struct cmp
{
bool operator() (int &a,int &b) const
{
return dis[a]>dis[b];
}
};
void dijkspfa(int x)
{
priority_queue<int,vector<int>,cmp>q;
int i,j,k;
memset(vis,0,sizeof(vis));
for(i=1;i<=n;++i)dis[i]=0x7f7f3f3f3f3f3f,stp[i]=0;
q.push(x);
dis[x]=0;
while(!q.empty())
{
k=q.top(),q.pop();
if(!vis[k])
{
vis[k]=1;
for(i=head[k];i;i=e[i].nxt)
{
int to=e[i].to;
if(dis[to]>=dis[k]*1.0+e[i].w/(stp[k]+1.0))
{
stp[to]=stp[k]+1;
dis[to]=dis[k]*1.0+e[i].w/stp[to]*1.0;
q.push(to);
}
}
}
}
}
int main()
{
int i,j,k,u,v,w;
cin>>n>>m>>s>>t;
for(i=1;i<=m;i++)
{
cin>>u>>v>>w;
add(u,v,w),add(v,u,w);
}
dijkstra(t);
cout<<(int)(ceil(dis[s]));
}

(虽然试试就逝世,但是出题人的数据怎么如此之氵,这也能骗30分)...

测评记录

  • 实际上,生命值不是 double 而是 intint有什么特性,自动向下取整。于是当一个数除以一个大于它的数时,值为 0
  • 因此可以让每条边被经过不超过 1000<=w<=100)遍。当一条边被经过 100 次时,求现有最小值与其值的最小值。最后输出即可
  • 话说 dijkspfa 是什么东西,怎么这么强。。。)。

附上AC代码

#include<bits/stdc++.h>
using namespace std;
int n,m,s,t;
struct aa
{
int nxt,to,w;
}e[1100001];
struct ee
{
int cnt,x;
}k;queue<ee>q;
int cnt=0,head[100001];
void add(int u,int v,int w)
{
e[++cnt]={head[u],v,w};
head[u]=cnt;
}
int dis[222][230011];
bool vis[222][230011];
int minn=0x7f7f7f00;
void dijkstra(int x)
{
int i,j;
memset(vis,0,sizeof(vis));
for(i=1;i<=101;++i)for(j=1;j<=n;++j)dis[i][j]=0x7f7f7f7f;
q.push({1,x});
dis[1][x]=0;
int v=0;
while(!q.empty())
{
k=q.front(),q.pop();
int y=k.cnt;
int p=k.x;
if(y>100)
{
minn=min(minn,dis[y][p]);
}
for(i=head[p];i;i=e[i].nxt)
{
int to=e[i].to;
if(dis[y+1][to]>=dis[y][p]*1.0+e[i].w/y)
{
dis[y+1][to]=dis[y][p]*1.0+e[i].w/y;
if(!vis[y+1][to])
q.push({y+1,to}),vis[y+1][to]=1;
}
}
}
}
int main()
{
int i,j,k,u,v,w;
cin>>n>>m>>s>>t;
for(i=1;i<=m;i++)
{
cin>>u>>v>>w;
add(u,v,w),add(v,u,w);
}
dijkstra(t);
for(i=1;i<=101;i++)minn=min(minn,dis[i][s]);
cout<<minn;
}

T4P9505 『MGOI』Simple Round I | D. 魔法环

0pts 一分没骗到。。。

  • 赛后我们首先查看题解,原来本蒟蒻与大佬之间的差距如此之大。
  • 由于精灵围成了一个环,因此首先考虑破环为链

石子合并

  • 状态转移方程()

  • fi,j=mink=1i1fk,j1+getv(k,i)

  • getv(l,r)表示这一步激活r点,上一个激活l点使当前值变化的量。

  • getv(l,r)=(rl+1)(rl)2×max(al,ar)+ar2

  • 这时复杂度为O(n4),轻轻松松TLE,观察状态转移方程可知,其于j即已激活点数无关,所以如果jk时可以直接从fk,j转移,此时复杂度为O(n3k)

  • 还有一个结论,即一定激活点0。因为如果未激活点0,由于至少要激活k(2k100)个精灵,因此未激活的精灵总能找到魔力值大于0的精灵。因此点0未产生贡献(甚至负贡献)。

  • 所以直接由0为起点,而此时复杂度为O(n2k),可以通过此题。

    • 关于大佬们的破环为链
for(int i=1;i<=n;i++) scanf("%d",&q[i]);
for(int i=1;i<=n;i++) if(!q[i]) pos=i;
for(int i=1;i<=n;i++) p[i]=q[(i+pos-1-1)%n+1];

关于我的破环为链

for(i=0;i<n;++i)
{
cin>>a[i];
if(!a[i])res=i;
}
for(i=res,j=1;j<=n;++i,++j)
{
s[j]=a[i];
if(i==n-1)i=-1;
}

(明显不如原神)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,s[3011],t,ans=0x7f7f7f7f,p;
int f[3022][111],a[3011];
inline int getv(int l,int r)
{
return ((r-l)*(r-l-1)>>1)*max(s[l],s[r])+s[r]*s[r];
}
signed main(void)
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
register int i,j,k;
int u,v,w,res;
cin>>n>>m;
for(i=0;i<n;++i)
{
cin>>a[i];
if(!a[i])res=i;
}
for(i=res,j=1;j<=n;++i,++j)
{
s[j]=a[i];
if(i==n-1)i=-1;
}
memset(f,0x7f,sizeof(f));
f[1][1]=0;
for(i=1;i<=n;++i)
for(j=2;j<=min(i,m);++j)
for(k=1;k<i;++k)
{
f[i][j]=min(f[i][j],f[k][j-1]+getv(k,i));
if(j==m)f[i][j]=min(f[i][j],f[k][j]+getv(k,i));
}
ans=LLONG_MAX;
for(i=1;i<=n;++i)ans=min(ans,f[i][m]+getv(i,n+1));
cout<<ans;
}
  • dp数组求最小值一定要初始化为\infin,并且将始状态赋值为0。
posted @   minecraft114514  阅读(115)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示