2025 SMU WINTER 3th

前言

内含牛客训5,6,vj round 10补题

牛客训5

L 构造恰有两队数互质的三元组

image

看题目的时候注意到是数据是1e6,暴力的去枚举的话肯定会t的。但我还是写了遍三次循环的,想着后续用什么来优化一下。后面发现不行,回归到了最开始的思路,三个一组,按偶奇偶排列,但是也不行,偶奇偶之后就是奇偶奇。。。
看题解说是:六个一组,image
(x+2,x+5都是3的倍数,豪强哇〒▽〒)

下面这个是我补题的时候不断wa不断修改的代码,有点丑。

点击查看代码
void solve() {
    ll n;cin>>n;
	ll ans=n/3;
	vector<ll>a;
//	cout<<ans<<'\n';
	if(n<=3)
	{
		cout<<0<<'\n';
		return;
	}else{
		cout<<ans<<'\n';
	}
	if((n%6)<=2)
	{
		for(ll i=1;i<=n;i+=6)
		{
			a.push_back(i);
			if(i+1<=n)	a.push_back(i+1);
			//	else break;
			if(i+3<=n)  a.push_back(i+3);
			//	else break;
			if(i+2<=n)  a.push_back(i+2);
			//	else break;
			if(i+4<=n)  a.push_back(i+4);
			//	else break;
			if(i+5<=n)  a.push_back(i+5);
			//	else break;
		}
		ll cnt=1;
		for(ll i=0;i<(ll)a.size();i+=3)
		{
			if(cnt<=ans)
			{
				cout<<a[i]<<" "<<a[i+1]<<" "<<a[i+2]<<'\n';
				cnt++;
			}else{
				break;
			}
		}
	}else{
		if(n>=4&&n<6)
		{
			cout<<"1 2 4\n";
		}else if(n>=6&&n<9)
		{
			cout<<"1 2 4\n3 5 9\n";
		}else
		cout<<"1 2 4\n3 5 9\n7 8 6\n";
		ll cnt=3;
		for(ll i=10;i<=n;i+=6)
		{
			a.push_back(i);
			if(i+1<=n)	a.push_back(i+1);
			//	else break;
			if(i+4<=n)  a.push_back(i+4);
			//	else break;
			if(i+2<=n)  a.push_back(i+2);
			//	else break;
			if(i+3<=n)  a.push_back(i+3);
			//	else break;
			if(i+5<=n)  a.push_back(i+5);
			//	else break;
		}
		for(ll i=0;i<(ll)a.size();i+=3)
		{
			if(cnt<ans)
			{
				cout<<a[i]<<" "<<a[i+1]<<" "<<a[i+2]<<'\n';
				cnt++;
			}else{
				break;
			}
		}
		
	}
}
好看版
void solve() {
	int n;
	cin>>n;
	if (n<=3) {
		cout<<0<<endl;
	}
	else if (n<=5) {
		cout<<"1"<<endl;
		cout<<"2 3 4"<<endl;
	}
	else {
		cout<<n/3<<endl;
		int c=1;
		if (n%6<3) {
			for (;c<=n/3*3;) {
				cout<<c<<" "<<c+1<<" "<<c+3<<endl;
				cout<<c+2<<" "<<c+4<<" "<<c+5<<endl;
				c+=6;
			}
		}
		else {
			cout<<"1 2 4\n3 5 9\n7 8 6\n";
			c=10;
			for (;c<=n/3*3;) {
				cout<<c<<" "<<c+1<<" "<<c+4<<endl;
				cout<<c+2<<" "<<c+3<<" "<<c+5<<endl;
				c+=6;
			}
		}
	}
}

E 小L的井字棋

image

样例1,答案为yes yes
2
OGX
XOO
GGX
GGG
GGG
GXO
样例2,答案为no
1
OXX
XOO
GGG

思路:分类讨论题。如果双方都没下或者下的次数小于2,那么直接输出yes;
否则,如果已给出的盘上有存在X的行或列或对角线有两空或一空(没O),则ok

点击查看代码
char mp[5][5];

bool check(ll x,ll y)
{
	bool ok=true;
	for(ll i=1;i<=3;++i)
    {
		if(mp[i][y]=='O')
		{
			ok=false;
			break;
		}
	}
	if(ok){
		return true;
	}
	
	ok=true;
	for(ll i=1;i<=3;++i)
	{
		if(mp[x][i]=='O')
		{
			ok=false;
			break;
		}
		
	}
	if(ok) return true;
	
//	ok=true;
	if(x==y)
	{
		ok=true;
        for(ll i=1;i<=3;++i)
		{
			if(mp[i][i]=='O')
			{
				ok=false;
				break;
			}
		}
		if(ok) return true;
	}
	if(x+y==4)   //反斜对角线表示
	{
		ok=true;
		for(ll i=1;i<=3;++i)
		{
			if(mp[i][4-i]=='O')
			{
				ok=false;
				break;
			}
		}
		if(ok) return true;
	}
	return false;
}

void solve()
{
    ll cnt=0;
	for(ll i=1;i<=3;++i)
	{
		for(ll j=1;j<=3;++j)
		{
		    cin>>mp[i][j];
			if(mp[i][j]=='X')
			{
				cnt++;
			}
		}
	}
	
	if(cnt>2)
	{
		for(ll i=1;i<=3;++i)
		{
			for(ll j=1;j<=3;++j)
			{
				if(mp[i][j]=='X'&&check(i,j))
				{
					cout<<"Yes\n";
					return;
				}
			}
		}
		cout<<"No\n";
	}else{
		cout<<"Yes\n";
		return;
	}
	cout<<"No\n";
}

I 小L的数学题

image

猜测大法
以后也可以多猜猜呢,别总是想着合理证明

点击查看代码
void solve()
{
	ll n,m;cin>>n>>m;
	if(n==0)
	{
		if(m==0)
		{
			cout<<"Yes\n";
			return;
		}else{
			cout<<"No\n";
		}
	}else{
		if(m==0)
		{
			cout<<"No\n";
		}else{
			cout<<"Yes\n";
		}
	}
}

牛客训6

A

反思:作为一个签到题,我的反应速度实在是有点慢了
我回想了下当时的状态
导致速度慢的原因有下

  1. 读题不认真,老毛病跳着读题。而且看着字多的“头晕”
  2. 思路浮现不清晰(感觉归根到底还是读题不认真的原因)

B好伙计猜拳

动态规划
前置知识是 用动态规划写最大上升子序列

点击查看代码
ll a[1010],b[1010],n,c1,c2,dp[1010][2];

void solve() {
    cin>>n>>c1>>c2;
	memset(dp,0x3f,sizeof(dp));
	dp[0][0]=0;
	ll ans=1e18;
	for(ll i=1;i<=n;++i)
	{
		cin>>a[i]>>b[i];
	}
	for(ll i=1;i<=n;++i)
	{
		for(ll j=0;j<=i-1;++j)
		{
			if(a[j]<=a[i]&&b[j]<=b[i])  
			//(aj,bj)后接(ai,bi)
			{
				dp[i][0]=min(dp[i][0],dp[j][0]+c1*(i-j-1));
			}
			if(b[j]<=a[i]&&a[j]<=b[i])
			//(bj,aj)接到(ai,bi)前
			{
				dp[i][0]=min(dp[i][0],dp[j][1]+c1*(i-j-1));
			}
		}
		for(ll j=0;j<=i-1;++j)
		{
			if(a[j]<=b[i]&&b[j]<=a[i])
	        //(aj,bj)接到(bi,ai)前
			{
	
				dp[i][1]=min(dp[i][1],dp[j][0]+c1*(i-j-1));
			}
			if(b[j]<=b[i]&&a[j]<=a[i])
			//(bj,aj)接到(bi,ai)前
			{
				dp[i][1]=min(dp[i][1],dp[j][1]+c1*(i-j-1));
			}
			
		}
		dp[i][1]+=c2;
		ans=min(ans,min(dp[i][0],dp[i][1])+c1*(n-i));
	}
	cout<<ans<<'\n';
}

小鸡的排列构造checker

赛时我做这道题,交了一次,tle了。也想不到什么优化的方法所以就放掉了
赛后看题解,题解展示了一个名叫离线将查询排列的技巧,使用树状数组维护。
image
详见代码

点击查看代码
void solve() {
    ll n,m;cin>>n>>m;
	vector<ll>p(n+1),pos(n+1);
	for(ll i=1;i<=n;++i)
	{
		cin>>p[i];
		pos[p[i]]=i;
	}
	vector<vector<ll>> q(n+1);
	vector<ll> l(m),r(m),c(m),ans(m);
	for(ll i=0;i<m;i+=1)
	{
		cin>>l[i]>>r[i]>>c[i];
		q[p[c[i]]].push_back(i);
	}
	vector<ll> bit(n+1);   //树状数组
	//树状数组每个节点bit[i]用来储存一个区间的累计值
	auto add=[&](ll i)    
	{
		//[&]是lambma的捕获列表
		//add是接收一个整数i的函数,作用是将bit数组的i位置上的值+1
		for( ;i<=n;i+=i&-i)bit[i]+=1;
	};
	auto sum=[&](ll i)
	{
		ll res=0;
		for(;i;i-=i&-i)
			/*
			i-=i&-i是树状数组种常见操作,
			通过低位的部分来递归地查询或更新数据结构地父节点
			
			i&-i取的是i和-i按位与的结果,代表当前i位置的“步长”或“跨度”
			
			*/
		{
		    res+=bit[i];
		}
		return res;
	};
	for(ll i=1;i<=n;++i)
	{
		for(ll j:q[i])
		{
			ans[j]=sum(r[j])-sum(l[j]-1)+l[j];
		
		}
			add(pos[i]);
	}
	for(ll i:ans)
	{
		cout<<i<<'\n';
	}
	
}

round 10

https://vjudge.net/contest/692274

C

思路:把每个ai加起来,求平方根,判断根是否为整数

判断是否整数代码
bool check(double x)
{
	ll temp=static_cast<ll>(x);
	return x==temp;
}

D

最开始做的那个版本结构有点模糊,绕来绕去的(感觉是substr用了两次并且没处理好),导致答案也错了。后面从头做了遍。
:因为从前往后的话,遇到的第一个元素是辅音,是无法判断是辅元辅还是辅元的。所以倒着来。如果倒数第二个数是辅音(题目已保障都能分割),那么就是辅元辅,last-=3;否则是辅元。依次存入开字符串类型的数组里。最后倒置数组,依次输出,并在每个字符串之后加'.'(除最后)

点击查看代码
bool check(char c)
{
	//V
	return c == 'a' || c == 'e'; 
}

void solve()
{
	ll n;cin>>n;
	string s;
	vector<string> tmp;
	string ans;
	cin >> s;
	ll last =n-1;
	while(last>=0)
	{
		if(check(s[last]))
		{
			tmp.push_back(s.substr(last-1,2));
			last -= 2;
		}
		else {
			tmp.push_back(s.substr(last-2,3));
			last -=3;
		}
	}
	
	int len = tmp.size();
	reverse(tmp.begin(), tmp.end());
	for(ll i=0;i<len;++i)
	{
		ans+=tmp[i];
		if(i!=len-1)
			ans.push_back('.');
	}
	cout<<ans<<'\n';
}

E

赛时做出来差了几步,判断差值是否成立。
我们要使奇数索引的前缀和等于偶数索引的前缀和
image

点击查看代码
void solve()
{
	ll n;cin>>n;
	vector<ll>a={0};
	ll oddpre[n+10]={0},evenpre[n+10]={0};
	map<ll,bool>mp;
	mp[0]=1;
	oddpre[0]={0};
	evenpre[0]={0};
	for(ll i=1;i<=n;++i)
	{
		ll x;cin>>x;
		a.push_back(x);
		
	}
	for(ll i=1;i<=n;++i)
	{
		oddpre[i]=oddpre[i-1];
		evenpre[i]=evenpre[i-1];
		if(i%2)
		{
			oddpre[i]+=a[i];
		}else{
			evenpre[i]+=a[i];
		}
//		cout<<oddpre[i]<<" "<<evenpre[i];
	}
	for(ll i=1;i<=n;++i)
	{
		ll temp=oddpre[i]-evenpre[i];
	//	cout<<temp<<'\n';
		if(mp[temp])
		{
			cout<<"YES\n";
			return;
		}else{
			mp[temp]=1;
		}
	}
	cout<<"NO\n";
	
}

我自己补题的时候出错是,我求奇偶前缀和是直接oddpre[i]=oddpre[i-1]+a[i]的这会导致,无法正确的累加到前缀和

总结

上周画的饼还是大饼哈,除开身体方面这周有点毛病,但直接还是效率不够的问题。
这周补的牛客题居多,见识到了很多新的方法(之前也没见过什么方法哈)。
再次感受到自己动态规划学的之烂,,,补题的时候感觉又在学新的一样〒▽〒
下周就是最后一周了,希望效率高点,多多补题,还有回顾动态规划,有多时间学下主席树(牛客6的I题也能用这个做)

posted @ 2025-02-11 00:01  YuanqLi  阅读(26)  评论(0)    收藏  举报