首页 > 其他分享 >2024年百度之星 初赛 第一场

2024年百度之星 初赛 第一场

时间:2024-06-04 16:12:24浏览次数:22  
标签:CI int back 初赛 2024 vec include 之星 RI

Preface

周末紧张刺激的两连赛,周六打完篮球杯后周日就要打百度之星的第一场省赛

由于第二场时间和四川省赛冲突了,第三场时间又在考试前一天,比较阴间,想着最好是一次打进决赛节省点钱和时间

虽然学校不报销去北京决赛的钱,但转念一想反正XCPC也都不报销也差不了多少,去北京旅个游也未尝不可

回到这场比赛,虽然总体来看感觉一堆Math Problem恰逢软肋(怎么和蓝桥杯一样),但好在比赛的时候选择开自己擅长的大分类讨论和大模拟,最后40minRush第六题也把思路出的七七八八了

最后写出前5题,总排Rank25,看了眼大学组里好像排Rank3,比较可惜的是和大学组Rank1的川大✌差了半小时的罚时,如果P3或者P5少WA两发就好了(但这种题不WA感觉是不可能的)

由于百度之星在自己电脑上存了代码,因此就和一般的题解一样顺便讲下做法吧,但现在题目还没公开因此题目名就先用个序号代替吧


P1

这题没啥好说的,看到数据范围就知道直接\(O(n^2)\)甚至\(O(n^2\log n)\)模拟一遍即可

当然由于每次只有一个位置会发生变化,理论上可以用平衡树/线段树等东西来做到\(O(n\log n)\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=1005;
int n,ans,B,p[N],s[N],w[N];
int main()
{
	RI i,j; for (scanf("%d%d",&n,&B),i=1;i<=n;++i) scanf("%d%d",&p[i],&s[i]);
	for (i=1;i<=n;++i)
	{
		for (j=1;j<=n;++j)
		if (i==j) w[j]=p[j]/2+s[j]; else w[j]=p[j]+s[j];
		sort(w+1,w+n+1); int cnt=0; long long sum=0;
		for (j=1;j<=n;++j)
		if (sum+w[j]<=B) sum+=w[j],++cnt; else break;
		ans=max(ans,cnt);
	}
	return printf("%d",ans),0;
}

P2

很有CF风格的一个题,难点在于求\(1\sim n\)的\(\operatorname{LCM}\),如果实现不够精细的话可能会在这里T掉

首先不妨设\(M=\operatorname{LCM}(1,2,\dots,n)\),则第\(i\)个人最后会跑\(\frac{M}{i}\)圈

显然对于两个人\(i,j(i<j)\),它们相遇的次数就是\(\frac{M}{i}-\frac{M}{j}\)

然后这个二维的求和式子很容易化成一维的,直接考虑第\(i\)个人的系数就是\(n-2i+1\),用这个值乘上\(\frac{1}{i}\)求和后,最后乘上\(M\)即可

现在的问题就是怎么快速计算\(M\),根据经典套路还是想到要把数分解质因数后比较好统计

对于质因数\(p_i\),不妨设\(1\sim n\)中它出现且指数最大值为\(c_i\),则其贡献就是\(p_i^{c_i}\)

可以先筛出\(1\sim n\)中所有的质数,然后暴力计算每个数最大的指数,这部分复杂度是\(O(\pi(n)\times \log n)\),差不多就是\(O(n)\)

最后总复杂度为\(O(n)\)

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=1e7+5,mod=998244353;
int n,M,fact[N],ifac[N],inv[N],ans,p[N],cnt,vis[N];
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
	RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
	for (ifac[n]=quick_pow(fact[n]),i=n-1;i>=0;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
	for (i=1;i<=n;++i) inv[i]=1LL*fact[i-1]*ifac[i]%mod;
}
inline void sieve(CI n)
{
	for (RI i=2;i<=n;++i)
	{
		if (!vis[i]) p[++cnt]=i;
		for (RI j=1;j<=cnt&&i*p[j]<=n;++j)
		if (vis[i*p[j]]=1,i%p[j]==0) break;
	}
}
int main()
{
	RI i; for (scanf("%d",&n),init(n),i=1;i<=n;++i)
	(ans+=1LL*inv[i]*(n-2*i+1+mod)%mod)%=mod;
	for (sieve(n),M=1,i=1;i<=cnt;++i)
	{
		int c=0; long long ret=1;
		while (ret*p[i]<=n) ret*=p[i],++c;
		M=1LL*M*quick_pow(p[i],c)%mod;
	}
	return printf("%d",1LL*ans*M%mod),0;
}

P3

这题其实没啥思维难度,因为就是纯爆搜,但由于写起来比较繁琐导致最后过的人就不是很多吧,反正很适合我这种写不来思维题的人去大力硬干

首先先大力BFS搜出连通块并判掉连通块数量\(>1\)的情况后,并且对于没有黑子的情况要人为地加一个子进去

由于数据范围很小,我们就考虑每次在一个合法的连通块的所有与之连通的格子中挑选一个扩展,因此现在问题集中在如何判断一个连通块是否和另一个同构

考虑用二维坐标来表示一个黑子的位置,并将所有黑子的位置存入vector

vector中的所有坐标排序后,将每个点的坐标统一减去第一个点的坐标,即可消除平移同构的影响

(因为这样相当于定下一个基准点并放到\((0,0)\)处,然后把所有点的坐标都转化为和它的相对位置,显然可以去除平移带来的影响)

接下来考虑旋转同构和对称同构,这个也很简单,直接用对应的变换公式把所有点的坐标变换一下即可

注意这些操作之间是可以组合的,因此我在实现时选择枚举顺时针旋转的次数\(r\in[0,3]\),水平镜像的次数\(fx\in[0,1]\),垂直镜像的次数\(fy\in[0,1]\)

算出对应的点集后用前面的方法去除平移的影响后,扔到set <vector <int>>里就可以判重了,最后输出这个set的大小即可

另外实现的时候还要注意连通块不能超出棋盘大小\(n\times m\),具体地只需判断点集横纵坐标的极差是否小于等于\(n,m\)即可

具体实现可以看代码,感觉思路还是很自然的,难点在于怎么优美地处理各种同构操作

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
#include<set>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=15,dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
struct Point
{
	int x,y;
	inline Point(CI X=0,CI Y=0)
	{
		x=X; y=Y;
	}
	friend inline bool operator < (const Point& A,const Point& B)
	{
		return A.x!=B.x?A.x<B.x:A.y<B.y;
	}
	friend inline Point operator - (const Point& A,const Point& B)
	{
		return Point(A.x-B.x,A.y-B.y);
	}
	inline void rotate(void)
	{
		int tx=x,ty=y; x=ty; y=-tx;
	}
	inline void flip_x(void)
	{
		y=-y;
	}
	inline void flip_y(void)
	{
		x=-x;
	}
};
int t,k,n,m,a[N][N],vis[N][N]; set <vector <Point>> rst;
inline void BFS(CI x,CI y)
{
	queue <Point> q; q.push(Point(x,y)); vis[x][y]=1;
	while (!q.empty())
	{
		auto [x,y]=q.front(); q.pop();
		for (RI i=0;i<4;++i)
		{
			int nx=x+dx[i],ny=y+dy[i];
			if (nx<1||nx>n||ny<1||ny>m) continue;
			if (a[nx][ny]==1&&!vis[nx][ny])
			q.push(Point(nx,ny)),vis[nx][ny]=1;
		}
	}
}
inline void normal(vector <Point>& vec)
{
	sort(vec.begin(),vec.end()); Point st=vec[0];
	for (auto &it:vec) it=it-st;
}
inline int calcx(vector <Point>& vec)
{
	int mnx=vec[0].x,mxx=mnx;
	for (auto [x,y]:vec) mnx=min(mnx,x),mxx=max(mxx,x);
	return mxx-mnx+1;
}
inline int calcy(vector <Point>& vec)
{
	int mny=vec[0].y,mxy=mny;
	for (auto [x,y]:vec) mny=min(mny,y),mxy=max(mxy,y);
	return mxy-mny+1;
}
inline bool check(vector <Point>& vec)
{
	int bx=calcx(vec),by=calcy(vec);
	if ((bx<=n&&by<=m)||(by<=n&&bx<=m))
	{
		for (RI r=0;r<4;++r)
		for (RI fx=0;fx<2;++fx)
		for (RI fy=0;fy<2;++fy)
		{
			vector <Point> tmp=vec;
			for (RI i=1;i<=r;++i)
			{
				for (auto &it:tmp) it.rotate();
			}
			if (fx)
			{
				for (auto &it:tmp) it.flip_x();
			}
			if (fy)
			{
				for (auto &it:tmp) it.flip_y();
			}
			normal(tmp);
			if (rst.count(tmp)) return 0;
		}
		return 1;
	} else return 0;
}
inline void DFS(vector <Point> vec,CI k)
{
	rst.insert(vec);
	if (k==0) return;
	set <Point> ext;
	for (auto it:vec) ext.insert(it);
	for (auto [x,y]:vec)
	{
		for (RI i=0;i<4;++i)
		{
			int nx=x+dx[i],ny=y+dy[i];
			if (ext.count(Point(nx,ny))) continue;
			vector <Point> tmp=vec; tmp.push_back(Point(nx,ny));
			normal(tmp); if (check(tmp)) DFS(tmp,k-1);
		}
	}
}
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; for (scanf("%d%d%d",&k,&n,&m),i=1;i<=n;++i)
		for (j=1;j<=m;++j) scanf("%d",&a[i][j]);
		int tot=0; Point st; memset(vis,0,sizeof(vis));
		for (i=1;i<=n;++i) for (j=1;j<=m;++j)
		if (a[i][j]==1&&!vis[i][j]) st=Point(i,j),BFS(i,j),++tot;
		if (tot>=2) { puts("-1"); continue; }
		vector <Point> vec;
		if (tot==0)
		{
			if (k==0) { puts("1"); continue; }
			--k; st=Point(0,0); vec.push_back(st);
		}
		for (i=1;i<=n;++i) for (j=1;j<=m;++j)
		if (a[i][j]==1) vec.push_back(Point(i,j)-st);
		normal(vec); rst.clear(); DFS(vec,k); printf("%d\n",rst.size());
	}
	return 0;
}

P4

这题比赛的时候写完前两题后看了眼榜就先跟了这个题,还是非常典的

看一眼数据范围可以\(O(n^2)\),那就直接大力设状态\(f_{i,j,p=0/1,q=0/1}\)表示处理了前\(i\)个位置,一共修改了\(j\)次,且当前\(a_{i-1}\)和\(a_i\)的值分别为\(p,q\)时的方案数

转移的话十分trivial就不再赘述,值得一提的是这题空间限制很小,需要把第一维滚存

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=5005,mod=998244353;
int n,m,f[2][N][2][2],a[N]; char s[N];
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
int main()
{
	RI i,j,p,q; scanf("%d%d%s",&n,&m,s+1);
	for (i=1;i<=n;++i) a[i]=s[i]-'0';
	if (n==1||n==2) return printf("%d",1<<min(n,m)),0;
	f[0][0][a[1]][a[2]]=1;
	f[0][1][a[1]^1][a[2]]=1;
	f[0][1][a[1]][a[2]^1]=1;
	f[0][2][a[1]^1][a[2]^1]=1;
	for (i=2;i<n;++i)
	{
		int now=i&1,nxt=now^1;
		for (j=0;j<=m;++j) for (p=0;p<2;++p) for (q=0;q<2;++q) f[nxt][j][p][q]=0;
		for (j=0;j<=m;++j) for (p=0;p<2;++p) for (q=0;q<2;++q)
		if (f[now][j][p][q])
		{
			if (!(p==1&&q==1&&a[i+1]==0))
			inc(f[nxt][j][q][a[i+1]],f[now][j][p][q]);
			if (!(p==1&&q==1&&a[i+1]==1))
			inc(f[nxt][j+1][q][a[i+1]^1],f[now][j][p][q]);
		}
	}
	int ans=0; for (i=0;i<=m;++i)
	for (p=0;p<2;++p) for (q=0;q<2;++q) inc(ans,f[n&1][i][p][q]);
	return printf("%d",ans),0;
}

P5

分类讨论你罪大恶极,写这种东西确实挺容易让人红温的,不过好在那天状态挺好WA了两发后就过了

首先一个大致的思路就是要把正数和负数分开,然后两边单独算出一个最大/最小值来算极差

考虑对于若干个符号相同的非负数(非正数的情况同理),如果数的数量\(\ge 2\),则最后能得到的最大数就是所有数之和再加上最大数;否则就只能得到那个数本身

因此我们首先可以看出\(0\)的作用来,我们首先搞出两个集合\(P,N\)分别放入正数和负数,同时记\(z\)表示\(0\)的数量

显然把\(0\)归到\(P,N\)中都是可以的,但不同的归法对上面式子的计算会有影响,不过好在我们稍微思考后就能发现每个集合中放入一个\(0\)和放入多个\(0\)是等价的,因此这部分可以枚举讨论一下

然后还有一大类情况就是没有正数/负数的情况,以没有负数为例

显然如果此时有\(0\)就直接钦定\(0\)为最小值,剩下部分用上述规则构造出最大值是最优的

否则分情况讨论,如果此时只有两个数,则答案为最大的那个正数,比如\(3,5\)这种情况,我们可以先操作一次把序列变成\(8,5\)

否则钦定最小的那个数为最小值,剩下部分用上述规则构造出最大值,相减得到的结果一定最优

然后写完后感觉自信满满直接提交,直接返回满屏的WA,遂开始反思漏了什么Corner Case

由于出错的情况肯定是数很少的时候,因此索性手动枚举各种情况,然后发现了\(-3,-2,-1,100\)这组数据

按照之前正负分开的规则最小能构造出\(-9\),最大就是\(100\)不变,最后答案就是\(109\)

但实际上我们可以先用\(-3,-2\)构造出\(-8\),再用\(-1,100\)构造出\(199\),最后答案就是\(207\)

这就提醒我们还需要讨论一种情况,即可以把每个集合中绝对值最小的那个数放到另一个集合中,看看答案是否会变大

然后写完交一发发现又挂了,后面手玩了下发现原来是只有一个正数和一个负数的情况就不能这样移动了,因此需要再特判一下移动后对应的集合不能为空

把上面的Case全部讨论清楚就可以通过此题了,上述做法仅代表我个人比赛时的思路过程,更加简单的分类方法应该也是有的,但只要能过题都是好方法

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
int t,n,x;
signed main()
{
	for (scanf("%lld",&t);t;--t)
	{
		RI i; vector <int> P,N; int zero=0;
		for (scanf("%lld",&n),i=1;i<=n;++i)
		{
			scanf("%lld",&x);
			if (x==0) ++zero; else
			if (x>0) P.push_back(x); else N.push_back(-x);
		}
		if (n==1) { puts("0"); continue; }
		if (P.empty()&&N.empty()) { puts("0"); continue; }
		sort(P.begin(),P.end(),greater <int>());
		sort(N.begin(),N.end(),greater <int>());
		auto calc=[&](vector <int>& vec,CI z)
		{
			int sum=0; for (auto x:vec) sum+=x;
			if (vec.size()+z>=2&&!vec.empty()) sum+=vec[0];
			return sum;
		};
		if (N.empty())
		{
			if (zero) printf("%lld\n",calc(P,zero-1));
			else
			{
				if (P.size()==2) printf("%lld\n",P[0]); else
				{
					int tmp=P.back(); P.pop_back();
					printf("%lld\n",calc(P,0)-tmp);
				}
			}
			continue;
		}
		if (P.empty())
		{
			if (zero) printf("%lld\n",calc(N,zero-1));
			else
			{
				if (N.size()==2) printf("%lld\n",N[0]); else
				{
					int tmp=N.back(); N.pop_back();
					printf("%lld\n",calc(N,0)-tmp);
				}
			}
			continue;
		}
		auto solve=[&](vector <int>& P,vector <int>& N)
		{
			int ret=0; for (RI i=0;i<=min(1LL,zero);++i)
			ret=max(ret,calc(N,i)+calc(P,zero-i)); return ret;
		};
		int ans=solve(P,N); vector <int> TP=P,TN=N;
		if (N.size()>1)
		{
			P.push_back(-N.back()); N.pop_back();
			sort(P.begin(),P.end(),greater <int>()); ans=max(ans,solve(P,N));
		}
		P=TP; N=TN;
		if (P.size()>1)
		{
			N.push_back(-P.back()); P.pop_back();
			sort(N.begin(),N.end(),greater <int>()); ans=max(ans,solve(P,N));
		}
		printf("%lld\n",ans);
	}
	return 0;
}

P6

感觉很有意思的一个构造题,比赛的时候想到了二分找最大的填数位置然后贪心往下检验,但没想到最后多出的数很少可以直接处理

主要感觉还是时间不足没有去跑一下\(10^{11}\)看下剩下来几个数的缘故

首先如果\(n\)比较小时有个很自然的做法,就是把数一个个加进去,初始时都默认加在\(1\)号位置

显然我们可以发现这样一个Rule,如果当前\(1\)号位置有至少\(2\)个数,且\(2\sim i\)号位置都有至少\(1\)个数,同时\(i+1\)号位置是空的

那我们显然可以把\(1\sim i\)这\(i+1\)个数全部放到\(i+1\)位置上,这个做法的正确性显然,用线段树维护每个位置的值,然后线段树上二分查询第一个\(0\)即可

但现在的问题是\(n=10^{11}\)没法直接这样处理,那我们不妨转换思路,根据题目的性质,下标最靠后的位置\(x\)填的数一定是\(x\)

而根据反字典序的比较规则,\(x\)的值是区分方案的第一要素,因此我们可以先利用二分找到最大的\(x\),并先构造一组合法的方案

验证的过程也很自然,给位置\(x\)填上\(x\)后,从高到低枚举每个位置,由于知道后面会有多少个数分下来,因此就能算出当前位置最少要填多少数才能满足要求,直接暴力往下处理即可

由于这样计算最少所需的数时,枚举的次数大致是\(O(\sqrt n)\)级别的,因此复杂度也是正确的

最后按照这样方法显然会多出一部分数,但实际跑一下后会发现\(n=10^{11}\)时大概会剩下\(300000\)个数,同时放到的最大的位置大概是\(560000\)左右

那么我们对多出的这部分直接跑一开始讲的那个做法即可,然后这题就做完了

不过由于题目还没开放同时接下来一堆期末考试,代码就先坑着了等题目放出来/考试考完后再来补吧


Postscript

剩下的两个题比赛的时候都没怎么看,后面看榜后感觉也不是我这种水平能做的题,暂且就先坑着了

这个学期剩下的比赛应该就只有6.18的省赛了,希望有银牌✌加持,把冠军留在UESTC吧

标签:CI,int,back,初赛,2024,vec,include,之星,RI
From: https://www.cnblogs.com/cjjsb/p/18231027

相关文章

  • 华为OD机试2024年最新题库(Python、JAVA、C、C++合集)C卷+D卷
    介绍博主介绍:CSDN领军人物top1的作者,全网粉丝30w+,文章累计被阅读3800w+,直接帮助200+,间接帮助800+同学进入od添加或私信博主免费获取本题解析以及代码24年5月份开始,考的都是OD统一考试(D卷),题库已经整理好了,命中率95%以上。5-10月份考的都是D卷真题,都是原题,圈内有多种......
  • 【TPAMI-2024】EfficientTrain++帮你降低网络训练的成本
    写在前面:本博客仅作记录学习之用,部分图片来自网络,如需引用请注明出处,同时如有侵犯您的权益,请联系删除!文章目录前言论文更容易学习的模式:频域易于学习的模式:空间域统一的训练课程EFFICIENTTRAIN++计算约束的顺序搜索高效低频下采样EfficientTrain++的实现技术实验......
  • 2024-05-14影视排行榜
    1、《种地吧第二季》2、《我的阿勒泰》3、《哈哈哈哈哈第四季》4、《幕府将军》5、《辐射》6、《惜花芷》7、《微暗之火》8、《金手指》9、《乘风2024》10、《歌手2024》(被大家说成五旬老太守国门。。。)11、《与凤行》12、《女高推理班第三季》13、《庆余年......
  • 【海外会议征稿通知】2024年第五届医学人工智能国际学术会议(ISAIMS 2024)
    2024年第五届医学人工智能国际学术会议(ISAIMS2024)20245th InternationalSymposiumonArtificialIntelligenceforMedicalSciences第五届医学人工智能国际学术会议(ISAIMS2024)将于2024年8月13-17日于荷兰阿姆斯特丹自由大学召开,同时在国内设置分会场。会议自2020年至......
  • 【EI会议征稿通知】第五届智能计算与人机交互国际研讨会(ICHCI 2024)
    第五届智能计算与人机交互国际研讨会(ICHCI2024)20245th InternationalConferenceonIntelligentComputingandHuman-ComputerInteraction第五届智能计算与人机交互国际研讨会(ICHCI2024)由南昌大学主办,将于2024年9月27-29日在中国南昌隆重召开。ICHCI2024的主题是“AI......
  • [毕设专用]2024最新梦想贩卖机,变现宝知识付费小程序(修改版本+前后端)
    梦想贩卖机升级版,变现宝吸取了资源变现类产品的很多优点,摒弃了那些无关紧要的东西,使本产品在运营和变现能力上,实现了质的超越。多领域素材资源知识变现营销裂变独立版。2024最新梦想贩卖机,变现宝知识付费小程序(修改版本+前后端)-资源吧——资源下载实现流量互导,多渠道变现。......
  • 手把手教你搭建:2024最新9.9付费进群程序(独家定位版本)有疑问找客服(附源码)
    单来讲就是搭建一套自动成交系统,作为从各大短视频引流到私域的一个变现工具以下图的五音不全K歌群为例,粉丝从短视频平台到公众号以后,只要打开系统链接,就可以自动付费,自动进群。原文转发自:近期爆火的流量掘金9.9自动进群系统玩法详细拆解-资源吧云服务器(CloudServer)云服......
  • 易支付系统新版【2024.5.27】正版【部署简单直接】
    今天我将详细介绍如何对接拉卡拉聚合收银台支付,并指出其中应注意的点。我希望这篇文章能够帮助那些正在寻找如何实现这个功能的开发者。一、拉卡拉聚合收银台支付简介拉卡拉聚合收银台支付是一种整合了多种支付方式的支付服务,包括但不限于微信支付、支付宝支付、银联支付等。......
  • 2024年机械工程、材料与制造技术国际会议(ICMEMMT 2024)
    2024InternationalConferenceonMechanicalEngineering,MaterialsandManufacturingTechnology【1】大会信息大会时间:2024-07-24大会地点:中国·深圳截稿时间:2024-07-10(以官网为准)审稿通知:投稿后2-3日内通知会议官网:http://www.icmemmt.com投稿邮箱:icmemmt@sub-......
  • 【JPCS出版|EI检索|最后一轮征稿】2024年机器人、自动化和控制工程国际会议(ICRACE 2024)
    2024年机器人、自动化和控制工程国际会议(ICRACE2024)会议官网:www.ic-race.org会议时间:2024年07月12-14日会议地点:中国-南京最终截稿时间:2024年6月28日报名截止时间:2024年7月05日征稿中(详情可与会务组老师联系)审稿回复时间:投稿后1周内提交检索类型:EICompendex,Scopus......