牛客周赛 Round 2题解(2023.7.9晚)

A题.小红的环形字符串

题意:小红拿到了一个环形字符串\(s\)。所谓环形字符串,指首尾相接的字符串。 小红想顺时针截取其中一段连续子串正好等于\(t\),一共有多少种截法?

分析:记\(s,t\) 的长度分别为\(n,m\) ,环形字符串可以被选取的子串等于\(s+s[n-1,n+m-1]\) ,然后得到了个新的字符串,等价问\(s'\) 能截取多少个\(t\) ,按\(m\) 分组统计即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;
int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	string s,t;
	cin>>s>>t;
	int n=s.size();
	for(int i=0;i<t.size()-1;i++)s+=s[(n+i)%n];
	int cnt=0;
	int i=0;
	while(i<s.size()){
		string k=s.substr(i,t.size());
		if(k==t)cnt++;
		i++;
	}
	cout<<cnt<<"\n";
	return 0;
} 

时间复杂度:\(O(nm)\)

空间复杂度:\(O(1)\)

B题.相邻不同数字的标记

**题意:**小红拿到了一个数组,每个数字被染成了红色或蓝色。小红有很多次操作,每次操作可以选择两个相邻的不同颜色的数字标记,并获得它们数字之和的得分。已经被标记的数字无法再次标记。小红想知道,自己最多能获得多少分。

**分析:**背包思想,令\(f(i,0)\) 为不选取\(a[i],a[i-1]\) 的最优解,\(f(i,1)\) 为选取了\(a[i],a[i-1]\) 的最优解,答案为\(max(f[n][1],f[n][0])\)

​ 考虑\(f(i,0)\) 的最优解状态转移,不选这个的最优解可能是上一个也不选\(f(i-1,0)\) ,也有可能上一个也选了\(f(i-1,1)\)

​ 考虑\(f(i,1)\) 的最优解状态转移,选这个的最优解就等于上一个没选的最优解加上\((a[i]+a[i-1])\) ,但注意如果\(color_i≠color_{i-1}\) 的话,\(f[i][1]=f[i-1][1]\) (比赛的时候被这个细节卡了半小时,原因是如果不继承的话会变成\(0\) )

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
using namespace std;
typedef long long ll;
const int N=1e5+7;
int a[N];
string c;
ll f[N][2];//第i个 选了前面那个为1 不选为0
int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	cin>>c;
	c=c[0]+c;//为了防止越界。
	for(int i=1;i<=n;i++){
		f[i][0]=max(f[i-1][1],f[i-1][0]); 
		//不选前面那个,这个废弃掉 
		if(c[i]!=c[i-1])f[i][1]=f[i-1][0]+a[i]+a[i-1];
		//选这个,就是前面没选的最优解+这次合并 
		else f[i][1]=f[i-1][1];
		
	}
	cout<<max(f[n][0],f[n][1]);
	return 0;
} 

C题.小红的俄罗斯方块

题意:见原题描述

**分析:**画下图模拟下就可以了,分别统计下不同的方块的不同触点会导致每列的高度怎么样的变化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
using namespace std;
typedef long long ll;
const int N=1e5+7;
int col[10];
int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	int tt;
	cin>>tt;
	while(tt--){
		int d,l;
		cin>>d>>l;
		if(d==0){
			int maxv=max(col[l],col[l+1]);
			col[l]=maxv+3;
			col[l+1]=maxv+1;
		}
		else if(d==90){
			int maxv=max(col[l]+1,max(col[l+1],col[l+2]));
			col[l]=maxv+1;
			col[l+1]=maxv+1;
			col[l+2]=maxv+1; 
		}
		else if(d==180){
			int maxv=max(col[l],col[l+1]+2);
			col[l]=maxv+1;
			col[l+1]=maxv+1;
		}
		else{
			int maxv=max(col[l],max(col[l+1],col[l+2]));
			col[l]=maxv+1;
			col[l+1]=maxv+1;
			col[l+2]=maxv+2;			
		}	
	}
	for(int i=1;i<=8;i++)cout<<col[i]<<" ";
	return 0;
} 

D题.小红打怪

题意:见原题描述

分析:求最优解问题,容易发现如果小红能杀\(k\) 只怪物,自然也能杀\(k-1,k-2,..\) 只怪物,如果他杀不了\(k\) 只,自然也杀不了\(k+1,k+2,..\)\(q=10^5\) ,单次回答的次数不能超过\(10^3\) ,但怪物数量就已经有\(10^5\) 了,所以不可能是\(O(n)\) 的做法,所以自然而然是二分答案了。

​ 先对怪物的血量和攻击力排序,然后二分到一个中点\(mid\) ,然后不断与每个怪物进行决策,但这样时间复杂度仍然是\(O(qnlogn)\),考虑将决策的时间复杂度降低至\(O(1)\) ,不难发现一个怪物的血量如果固定了,那打他需要的次数也固定了,随之扣的血量也固定了,因此定义\(a[i]\) 为前\(i\) 个怪物消灭他们的受伤值,这样只需要二分最大一个小于\(h+xk\) 的即可(注意不是等于号!因为如果恰好等于的话说明最后一次已经阵亡了

​ 一个怪物血量为\(h\) ,攻击力为\(p\) 时,我们最优的攻击序列是\([2,1,1,2,1,1,..]\) ,令\(t=4\) ,则怪物可以经历完整的\(3*(\frac{t}{4})\) 次攻击,剩余可能是\(1,2,3\) ,分别有\([1,1,2]\) 次,然后观察可以发现受伤值=攻击次数\(-1\) (最后一次杀完了不扣血),处理下即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <algorithm> 
using namespace std;
typedef long long ll;
const int N=1e5+7;
ll a[N];//前i个怪物的伤害 
ll n,h,k,x;
int ag[4]={0,1,1,2};


void solve(){
	int l=0,r=n;
	ll heal=h+x*k;
	while(l<r){
		int mid=(l+r+1)>>1;
		if(a[mid]<heal)l=mid;//满足条件的mid 往大了找 
		else r=mid-1;
	}
	cout<<l<<" ";
	return;
}

int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin>>n>>h>>k;
	for(int i=1;i<=n;i++){
		ll h,p;
		cin>>h>>p;
		ll d=h/4*3;//这里记得开long long
		ll d1=h%4;//这里记得开long long
		d+=ag[d1];
		d--;
		a[i]=d*p; 
	}
	sort(a+1,a+1+n);//排序
	for(int i=1;i<=n;i++)a[i]+=a[i-1];
	int tt;
	cin>>tt;
	while(tt--){
		cin>>x;
		solve();
	}
	return 0;
} 

时间复杂度:\(O(nlong+qlogn)\)

空间复杂度:\(O(n)\)

comments powered by Disqus
Built with Hugo
主题 StackJimmy 设计