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\),使得 \(2^m<n\)

  • 又双叒叕是个水题,然后被又双叒叕水题爆切。。。。。。
  • 只要判断 \(log2(n)\%2\) 是否为偶数,并且不能正好是 \(2\)\(n\) 次方,然后输出即可。
#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\),那么经过这条边时发生的战斗将会消耗 \(\left\lfloor \dfrac{w}{k} \right\rfloor\)生命值。魔兽不会被打败,因此多次经过同一条边,每次都会发生战斗

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

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

【数据范围】

对于所有数据,\(1 \le n \le 20000\)\(1 \le m \le 40000\)\(1\le s,t,u,v\le n\)\(s\ne t\),图为无向简单连通图,\(0\le w\le 100\)

Subtask \(n\) \(m\) \(w\le\) 分值
\(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\) 而是 \(int\)\(int\)有什么特性,自动向下取整。于是当一个数除以一个大于它的数时,值为 \(0\)
  • 因此可以让每条边被经过不超过 \(100\)\(0<=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\) 一分没骗到。。。

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

石子合并

  • 状态转移方程\(\LARGE \textcolor {#9624f6} {(由大佬的题解得出)}\)

  • \(\Large f_{i,j}=\displaystyle\min_{k=1}^{i-1}f_{\substack{k,j-1}}+getv(k,i)\)

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

  • \(\Large getv(l,r)=\cfrac{(r-l+1)(r-l)}{2}\times \max(a_{l},a_{r})+a{^2_r}\)

  • 这时复杂度为\(\large O(n^4)\),轻轻松松\(\Huge \textcolor {#111111} {TLE}\),观察状态转移方程可知,其于\(j\)即已激活点数无关,所以如果\(\large j\ge k\)时可以直接从\(\large f{_k} { _,} {_j}\)转移,此时复杂度为\(\large O(n^3k)\)

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

  • 所以直接由\(0\)为起点,而此时复杂度为\(\large O(n^2k)\),可以通过此题。

    • 关于大佬们的破环为链
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 @ 2023-08-06 19:26  minecraft114514  阅读(108)  评论(0编辑  收藏  举报