2025 SMU WINTER 1th

前言

内含2025 smu winter round1,2,3,4 + 总结

比赛

SMU Winter 2025 Round 1

https://vjudge.net/contest/686497

02 Yes...中找子字符串

这个题目是先把后面的题目写了才反过来写这个的,因为最开始做交了一发然后wa了(T T)
第一次交他反馈给我的是wa2,我猜测是初始化的s0的Yes长度可能不太够,因为题目输入的最大字符串长度是50,我就整好输出的是17个
又多加了几个Yes,ac了(Y ^ _ ^ Y)

(贴上主体代码)
哦对,我这回用上了字符串里面的find函数,感觉很好用哇咔咔s0.find()!=string::npos

CLICK(Y ^ _ ^ Y)
void solve()
{
    ll t;cin>>t;
	string s0="YesYesYesYesYesYesYesYesYesYesYesYesYesYesYesYesYesYesYesYesYesYes";
	while(t--)
	{
		string s;cin>>s;
		if(s0.find(s)!=string::npos)
		{
			cout<<"YES"<<endl;
		}else{
			cout<<"NO"<<endl;
		}
	}
}

后面有个学长发了他的解题思路捏
这样就不用管原字符串的“任意”长度了

CLICK(Y ^ _ ^ Y)
#include<bits/stdc++.h>
using namespace std;
int main()
{
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	int t;cin>>t;
	char nxt[300]{};
	nxt['Y']='e',nxt['e']='s',nxt['s']='Y';
	while(t--)
	{
		string s;cin>>s;
		s+=nxt[s.back()];
		int mark=1;
		for(int i=0;i+1<s.size()&&mark;++i)
		{
			if(!nxt[s[i]]||nxt[s[i]]!=s[i+1]) mark=0;
		}
		if(mark) cout<<"Yes\n";
		else cout<<"No\n";
	}
}

04 丢失的序列

这道题比赛的时候没ac,〒▽〒
我后面发现我的思路一直是执着于一个一个去遍历,去构造满足条件的序列,在判断yes or no,其实只用判断是否能就行了,不用一个一个详细的构造
之前有回也是这样T_T

CLICK(Y ^ _ ^ Y)
void solve()
{
	int m,s;cin>>m>>s;
    int a[m+1]={0};

	int ans=0;
	for(int i=1;i<=m;++i)
	{
		cin>>a[i];
	}
	sort(a+1,a+1+m);
	int cnt[a[m]+1]={0};
	for(int i=0;i<=m;++i)
	{
		cnt[a[i]]++;
	}
	for(int i=1;i<=a[m];++i)
	{
		if(cnt[i]==0)
		{
			ans+=i;
		}
	}
	if(ans>s)
	{
		cout<<"No\n";
		return;
	}else{
		s-=ans;
		int temp=a[m]+1;
		while(s>0)
		{
			s-=temp;
			temp++;
		}
		if(s==0)
		{
			cout<<"Yes\n";
			return;
		}else{
			cout<<"No\n";
			return;
		}
	}
}
补题的时候用了下vector,但是好生疏啊。。得多用用呢

05 除自己之外与最大者的差值

在第一次遍历的时候就把最大值,和第二大的值给找出来,因为后面判断有两种情况——判断元素是最大值or不是最大值

还有就是对我自己而言,最大值和第二大值的判断还是得注意

CLICK ^ _ ^
if(maxx1<a[i])
	{
		maxx2=maxx1;
		maxx1=a[i];
	}else if(a[i]>maxx2)
	{
		maxx2=a[i];
	}

06 最小调温度次数

比赛的时候看这道题晕头转向的,赛后听讲解知道原来是这么想的
要分几种情况

  • 首先特判:看a是否直接等于b
  • 然后看a-x,a+x,b-x,b+x是否在l和r的范围里面
  • 如果在,再计算a和b的距离
  • 距离>=s ---> 一次 (结合题意)
    dis>0 & dis<0
点击查看代码
void solve()
{
    ll l,r,x;cin>>l>>r>>x;
	ll a,b;cin>>a>>b;
	if(a==b)
	{
		cout<<"0"<<endl;
		return;
	}
	int x1=b-x;int x2=b+x;
	int ck1=0,ck2=0;
	if(x1<l) ck1++;
	if(x2>r) ck2++;
	if(ck1&&ck2){
		cout<<"-1"<<'\n';
		return;
	}
	int y1=a-x;int y2=a+x;
	int ck3=0,ck4=0;
	if(y1<l) ck3++;
	if(y2>r) ck4++;
	if(ck3&&ck4)
	{
		cout<<"-1"<<endl;
		return ;
	}
	int dis=a-b;
	if(fabs(dis)>=x)
	{
		cout<<"1"<<endl;
		return ;
	}
	if(dis>0)
	{
		if(ck1==0)
		{
			cout<<"2"<<endl;
			return ;
		}
		if(ck2==0)
		{
			if(ck4==0)    //这里要注意ck3和ck4的判断顺序
			{
				cout<<"2"<<endl;
				return;
			}
			if(ck3==0)
			{
				cout<<"3"<<endl;
				return ;
			}
		}
	}
	if(dis<0)
	{
		if(ck2==0)
		{
			cout<<"2"<<endl;
			return;
		}
		if(ck1==0)
		{
			if(ck3==0)
			{
				cout<<"2"<<endl;
				return ;
			}
			if(ck4==0)
			{
				cout<<"3"<<endl;
				return ;
			}
		}
	}
}

07 两倍字符串构造回文

其实很好想的,直接把倒叙的字符串加到原来的字符串上就ok了
为什么交的第一次wa了呢〒▽〒因为lyq把'\0'也算进去了。。

08 三种山谷判断

  1. 升序
  2. 降序
  3. mid 这里使用缩点

缩点豪强哇〒▽〒

缩点

首先开一个动态数组
然后先把当前元素存入数组,判断当前元素是否等于下一个元素,
如果是,用while循环接着判断后面的也是不是等于,
如果等于就“跳过”
这样就能把一段线段缩成一个点

点击查看代码
#define int long long

void solve()
{
    int n;cin>>n;
	int a[n+10]={0};
	for(int i=0;i<=n-1;++i)
	{
		cin>>a[i];	
	}
	
	//up
	bool up=true;
	for(int i=0;i<=n-2;++i)
	{
		if(a[i]>a[i+1])
		{
			up=false;
			break;
		}
	}
	if(up)
	{
		cout<<"Yes\n";
		return;
	}
    
	//down
	bool down=true;
	for(int i=0;i<=n-2;++i)
	{
		if(a[i]<a[i+1])
		{
			down=false;
			break;
		}
	}
	if(down)
	{
		cout<<"Yes\n";
		return ;
	}
	
	//mid 缩点
	vector<int>s;
	for(int i=0;i<=n-2;++i)
	{
		s.push_back(a[i]);
		if(a[i]==a[i+1])
		{
			int j=i+2;
			while(a[j]==a[i] && j<=n-1)
			{
				j++;
			}
			i=j-1;
		}
	}
	if(a[n-1]!=a[n-2])
	{
		s.push_back(a[n-1]);
	}
	
	n=s.size();
	int cnt=0;
	if(s[1]>s[0]) cnt++;
	if(s[n-2]>s[n-1]) cnt++;
	for(int i=1;i<=n-2;++i)
	{
		if(s[i]<s[i-1] &&s[i]<s[i+1])
		{
			cnt++;
		}
	}
	if(cnt==1){
		cout<<"Yes\n";
		return;
	}else{
		cout<<"No\n";
		return;
	}
}

09 最大逆序数对

题目有说明最多一次操作
所以就有三种情况,存入三个数组之后,按三种情况依次遍历即可
但是要注意b数组和c数组分别要进行的操作的处理(倒序即可)
最后比较三个操作的最大值

点击查看代码
void solve()
{
    int n;cin>>n;
	int a[n+10]; //不变
	int b[n+10]; //1变0
	int c[n+10]; //0变1
	for(int i=1;i<=n;++i)
	{
		int x;cin>>x;
		a[i]=x;
		b[i]=x;
		c[i]=x;
	}
	
    int suma=0;
	int cnta=0;
	for(int i=n;i>=1;--i)    //从n到1
	{
	    if(a[i]==0)
		{
			cnta++;
		}else{
			suma+=cnta;
		}
	}
	
	int sumb=0;
	int cntb=0;
	for(int i=n;i>=1;--i)
	{
		if(b[i]==1)
		{
			b[i]=0;
			break;
		}
	}
	for(int i=n;i>=1;--i)
	{
		if(b[i]==0)
		{
			cntb++;
		}else{
			sumb+=cntb;
		}
	}
	
	int sumc=0;
	int cntc=0;
	for(int i=1;i<=n;++i)
	{
		if(c[i]==0)
		{
			c[i]=1;
	    	break;
		}
	}
	for(int i=n;i>=1;--i)
	{
	    if(c[i]==0)
		{
			cntc++;
		}else{
			sumc+=cntc;
		}
	}
	
	int maxx=max(max(suma,sumb),sumc);
	cout<<maxx<<'\n';
}

12 XOR = Average

如果n是奇数,那么可以构造全为1的数列
如果n为偶数,要使得

要使等式成立,那么则数列的和要是n的倍数,假使为2 2 2 2,其实就相当于1 1 1 1了,异或答案为0,等式不成立,但是如果修改成2 2 1 3,则成立,左边异或为2(1和3异或结果是2)

点击查看代码
void solve()
{
    int n;cin>>n;
	if(n%2==1)
	{
		while(n--)
		{
			cout<<"1 ";
		}
		cout<<'\n';
	}else{
		cout<<"1 3 ";
		n-=2;
		while(n--)
		{
			cout<<"2 ";
		}
		cout<<'\n';
	}
}

SMU Winter 2025 Round 2

https://vjudge.net/contest/687074#overview
这回时间还是很紧张的,两个小时。lyq还是体会到了不到最后不能放弃哈。我做完b题之后其实是把c题跳了的,感觉d题好像能写出来
然后就在那吭哧敲半天,最后发现方法给用错了。。。
实在不想用两题收尾,最后十六分钟开始重新看c题,把c写出来了(T _ T)

01 字符串数据比较

题意就是字符串前三个和后三个比,看是否相同
还有要注意的是输入数据是挨在一起的,如果int的话,会出现错误,我觉得应该是int字节数和char字节数的差异导致的
(lyq开的是数组)

02 最少糖果数量

看清题目哇!lyq以为可以重新分配每个盒子里面的糖果数量。。找到输入数据中的minn,然后sum-minn*n就得

03 最小差异字符串对

最开始阻碍我的是如何在输入的字符串中找到最小对
后面发现两层for循环嵌套就行了,( •̀ ω •́ )y

CLICK(❁´◡`❁)
int cmp(string s1,string s2,int m)
{
	int ans=0;
	for(int i=0;i<=m;++i)
	{
		if(s1[i]!='\0'&&s2[i]!='\0')
		{
			int temp=abs(s2[i]-s1[i]);
			ans+=temp;
		}
	}
	return ans;
}

int main()
{
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	int t;cin>>t;
	
	while(t--)
	{
		string s[55];
		int n,m;cin>>n>>m;
		for(int i=1;i<=n;++i)
		{
			cin>>s[i];
		}
		int minn=INT_MAX;
		for(int i=1;i<n;++i)
		{
			for(int j=i+1;j<=n;++j)
			{
				int sum=cmp(s[i],s[j],m);
				minn=min(sum,minn);
			}
		}
		cout<<minn<<endl;;
	}
	return 0;
}

04 主教攻击最大和

稀里糊涂写半天,,,
后面听别人的思路,lyq你还是多学学点吧〒▽〒
用while+坐标移动来解决对角线

CLICK(❁´◡`❁)

void solve()
{
	int n,m;cin>>n>>m;
	int a[210][210];
	int g[210][210];
	int maxx=-1;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			a[i][j]=0;
			g[i][j]=0;
		}
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			cin>>a[i][j];
		}
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			int u=i,v=j;
			while(u!=1&&v!=1)
			{
				u--;
				v--;
			}
			while(u!=n&&v!=m)
			{
				g[i][j]+=a[u][v];
				u++;
				v++;
			}
			g[i][j]+=a[u][v];
			u=i,v=j;
			while(u!=1&&v!=m)
			{
				u--;
				v++;
			}
			while(u!=n&&v!=1)
			{
				g[i][j]+=a[u][v];
				u++;
				v--;	
			}
			g[i][j]+=a[u][v];
			g[i][j]-=a[i][j];
			if(g[i][j]>=maxx)
			{
				maxx=g[i][j];
			}
		}
	}
	cout<<maxx<<endl;
}

05 最少吃糖数量

前缀和+贪心+二分

点击查看代码
void solve()
{
    int n,q;cin>>n>>q;
	int a[n+10];
	int sum[n+10];
	for(int i=1;i<=n;++i)
	{
		cin>>a[i];
	}
	sort(a+1,a+1+n,greater<>());
	sum[0]=0;
	for(int i=1;i<=n;++i)
	{
		sum[i]=sum[i-1]+a[i];
	}
	while(q--)
	{
		int x;cin>>x;
		int l=1;
		int r=n;
		int ans=-1;
		while(l<=r)
		{
			int mid=(l+r)/2;
			if(sum[mid]>=x)
			{
				ans=mid;
				r=mid-1;
			}else{
				l=mid+1;
			}
		}
		if(ans>=1&&ans<=n)
		{
			cout<<ans<<'\n';
	//		return;
		}else{
			cout<<"-1\n";
	//		return;
		}
	}
}

03 SMU Winter 2025 Round 3

https://vjudge.net/contest/687465#overview

01 睡觉时间

跨天的问题,通常都让lyq头疼一会
但这次感觉 化成分钟 这种方法能迁移到各个跨天的问题(?)
但是比赛的时候交的不知为什么就t掉了,,,后面改了点,莫名其妙又过了。。。

02 不同数字的序列

因为只能从数列的左边删除元素,所以在最后一个重复元素之前的元素都要去掉
所以我们不妨倒序,然后如果遇到了第一个重复元素,就break掉,没遇到就把当前元素存到数组里面

03 s的不同组成数字

贪心的运用
每次都从目前最大的的看,如果s>=i,那么就减去,并且纳入数组

点击查看代码
void solve()
{
	int s;cin>>s;
	vector<int>a(10);
	for(int i=9;i>=1;--i)
	{
		if(s>=i)
		{
			a.push_back(i);
			s-=i;
		}
	}
	sort(a.begin(),a.end());
	for(auto t:a)
	{
		if(t!=0)
		cout<<t;
	}
	cout<<'\n';
}

04 最低的第一名

在纸张推算几次,会发现三种情况,%n得0,1,2

点击查看代码
void solve(){
    int n;cin>>n;
	int ave=n/3;
	if(n%3==0)
	{
		cout<<ave<<" "<<ave+1<<" "<<ave-1<<'\n';
		return;
	}
	if(n%3==1)
	{
		if(ave>2)
		cout<<ave+1<<" "<<ave+2<<" "<<ave-2<<'\n';
		else
			cout<<ave<<" "<<ave+2<<" "<<ave-1<<'\n';
	    return ;
	}
	if(n%3==2)
	{
		cout<<ave+1<<" "<<ave+2<<" "<<ave-1<<'\n';
		return;
	}
}

05 n次操作后数组是否相同

找到两个数组差最大的,然后让每个元素-=maxx,
如果有负数,那么就再复制为0
如果最后处理后的数组和目标数组一样那么就ok

点击查看代码
#define int long long

void solve()
{
	int n;cin>>n;
	int a[n+10];
	int b[n+10];
	int d[n+10];
	for(int i=0;i<n;++i)
	{
		cin>>a[i];
	}
	for(int i=0;i<n;++i)
	{
		cin>>b[i];
	}
	int maxx=-1;
	for(int i=0;i<n;++i)
	{
		if(a[i]-b[i]>=0)
	    {
			d[i]=a[i]-b[i];
			maxx=max(d[i],maxx);
		}
		else{
			cout<<"No\n";
			return;
		}
	}
	bool ok=true;
    for(int i=0;i<n;++i)
	{
		a[i]-=maxx;
		if(a[i]<0)
		{
			a[i]=0;
		}
		if(a[i]==b[i])
		{
			ok=1;
		}else{
			ok=false;
			cout<<"No\n";
			return;
		}
		
	}
	if(ok==true)
	cout<<"Yes"<<"\n";
}

07 黑白方格

这个和学姐当时上课给我们讲的前缀和的那个修路灯问题好像好像
所以就用前缀和做了

点击查看代码
#define int long long

void solve()
{
    int n,k;cin>>n>>k;
	int w[n+10]={0};
	int qz[n+10]={0};
	for(int i=1;i<=n;++i)
	{
		char x;cin>>x;
		if(x=='W')
		{
		    w[i]=true;	
		}
	}
	for(int i=1;i<=n;++i)
	{
		qz[i]=qz[i-1];
		if(w[i])
		{
			qz[i]++;
		}
	}
	int minn=INT_MAX;
	for(int i=k;i<=n;++i)
	{
		minn=min(qz[i]-qz[i-k],minn);
	}
	cout<<minn<<endl;
}

08 abc非递减顺序


画相同颜色的下划线的元素可以互换顺序
分为集团来看,要是达到非递减顺序(也就是前一个元素大于等于后一个元素。依次递减是指前一个元素大于后一个元素),也就是要前一个集团的最大值小于等于后一个集团的最小值

点击查看代码
#define int long long
void solve()
{
    int n;cin>>n;
	int a[n+10];
	for(int i=1;i<=n;++i)
	{
		cin>>a[i];
	}

	bool ok=false;
	
	if(n==1)
	{
		cout<<"Yes\n";
		return;
	}
	if(n==2)
	{
		cout<<"Yes\n";
		return;
	}
	if(n%2)
	{
		int t=a[1];
		for(int i=2;i<=n;i+=2)
		{
			if(min(a[i],a[i+1])>=t)
			{
				ok=true;
			}else{
			    ok=false;
				cout<<"No\n";
				return;
			}
			t=max(a[i],a[i+1]);
		}
		if(ok)
		{
			cout<<"Yes\n";
			return;
		}
		
	}
	if(n%2==0)
	{
		int t=max(a[1],a[2]);
		for(int i=3;i<=n;i+=2)
		{
			if(min(a[i],a[i+1])>=t)
			{
				ok=true;
			}else{
				ok=false;
				cout<<"No\n";
				return;
			}
			t=max(a[i],a[i+1]);
		}
		if(ok)
		{
			cout<<"Yes\n";
			return;
		}	
	}
}

SMU Winter 2025 Round 4

https://vjudge.net/contest/687995

05 区间求和

不能暴力解,会t
开个vis记录时间,如果vis[i]==dq(时间相同(不依赖于tot))就直接在原数组上加减
否则就算int temp=x-tot,然后让sum+=temp

点击查看代码
void solve()
{
    ll n,q;cin>>n>>q;
	ll a[n+10];
	ll sum=0;
	for(ll i=1;i<=n;++i)
	{
		cin>>a[i];
	    sum+=a[i];
	}
	ll vis[n+10]={0};
	ll tot=-1;
	ll dq=0;
	while(q--)
	{
		ll t;cin>>t;
		if(t==1)
		{
		    ll i,x;cin>>i>>x;
			if(vis[i]==dq)
	    	{
				ll temp=x-a[i];
				a[i]=x;
				sum+=temp;
			}
			else{
				ll temp=x-tot;
                sum+=temp;
				a[i]=x;
				vis[i]=dq;
			}
			cout<<sum<<'\n';
			
		}else{
			ll x;cin>>x;
			tot=x;
			sum=n*x;
			dq++;
			cout<<sum<<'\n';
		}
	}
}

06 攻击棋盘

因为棋盘的一条边就有1e5的数据,所以不能存二维数组
开两个set,存储安全的,没有被攻击到的行和列

点击查看代码
#define int long long

void solve()
{
    int n,q;cin>>n>>q;
	set<int>h,l;
	vector<int>sumh(n+1),suml(n+1);  //行or列被攻击的次数
	for(int i=1;i<=n+2;++i)
	{
		h.insert(i);   //没被攻击的行
		l.insert(i);   //没被攻击的列
	}
	while(q--)
	{
		int op;cin>>op;
		if(op==1)
		{
			int x,y;cin>>x>>y;
			h.erase(x);
			l.erase(y);
			sumh[x]++;
			suml[y]++;
		}
		else if(op==2)
		{
			int x,y;cin>>x>>y;
			sumh[x]--;
			suml[y]--;
			if(sumh[x]==0) 
			{
				h.insert(x);
			}
			if(suml[y]==0)
			{
				l.insert(y);
			}
		}
		else
		{
			int x1,y1,x2,y2;
			cin>>x1>>y1>>x2>>y2;
			int temp1=*h.lower_bound(x1);
			int temp2=*l.lower_bound(y1);
			if(temp1>x2||temp2>y2)
			{
				cout<<"Yes\n";
			}else{
				cout<<"No\n";
			}
		}
	}
}

总结

这一周以比赛+补题为主。(但是还没补完哈〒▽〒)通过这一周的练习,我能感受到自己面对问题的思考能力和代码能力有在提升(❁´◡`❁)思维也变好了点。
同时我还是发现自己连续wa之后会急躁,maybe过一会在看思路能捋清~(详见r5的g题〒▽〒
还有就是算法的运用还是浮在浅浅的一层。还是要多练练哇(●'◡'●)
下周要学习下线段树,两次遇见它了,还有要写题单里的题目。

posted @ 2025-01-18 21:10  YuanqLi  阅读(59)  评论(0)    收藏  举报