牛客周赛 Round 56详细题解(A~F)附有不同方法

前言

还是要写题解啊,孩子们,真不是为了别人,是为了未来的你自己啊,今天开始立志每场比赛都写题解,记录下那巧思,真的很重要!!!(我觉得)

正文

A  面包店故事

打卡题

#include <bits/stdc++.h>
#define N 1000001   //宏定义一个最大值
#define ll long long
using namespace std;    //命名空间
int main() {
	int x,y,n;
	cin>>x>>y>>n;
	if(x+y<=n)cout<<"YES"<<endl;
	else cout<<"NO"<<endl;
}

B 放课后故事

这个主要是题意问题

先算出来能叠多少飞机,然后只有和他最后一起玩的才能分到飞机(注意还有他自己),然后记得开long long,就过了

#include <bits/stdc++.h>
#define N 1000001   //宏定义一个最大值
#define ll long long
using namespace std;    //命名空间
ll a[N];

int main() {
	ll x, y, n, m, k, ans, sum = 0;
	cin >> n >> m >> k;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	for (int i = 0; i < n; i++)
		sum += a[i];
	sum /= k;
    if(sum)sum=min(sum,m+1);
	cout << sum << endl;
}

C 异或故事

这个题的通过率比D题都少,还是很多坑的,苯人赛时wa7遍

这道题赛时写了两种解法粗来,赛后见到一个牛的暴力下面都写一下

C-slove1

首先我们可以看一下异或的三个性质

x^0=x 任何数和0异或为它本身

x^x=0 任何数和自己异或都为0

(a^b)^c=a^(b^c) 异或有交换律

然后题干上给的条件是两个数 a,b需要在1~1e9之间,我们不妨将一个数b设为1

那么所给数是x=a^b=a^1

然后有x^1=a^(1^1)=a^0=a

所以另一个数a就是x^1

这是最基础的思路,但是我们需要结合题干所给范围1~1e9

当x=1是b=1,a=1^1=0,就不满足条件,所以需要特判

当x=1e9时b=1,a=1e9^1=1e9+1(可以算一下)a超过了1e9,所以也要特判,然后就ac了

#include <bits/stdc++.h>
#define N 1000001   //宏定义一个最大值
#define ll long long
using namespace std;    //命名空间
int a[N];

int main() {
	int x, y, n, m, k, ans, sum = 0, t;
	cin >> t;
	while (t--) {
		cin >> x;
        if(x==1){cout<<"2 3"<<endl;continue;}
        if(x==1000000000){cout<<512<<' '<<x-512<<endl;continue;}
		x=x^1;
		cout << 1 << ' ' << x << endl;
	}
}

C-slove2

在上面特判1e9时 a,b怎么找呢,我想到了位运算,首先用计算器可以找到1e9的二进制数

0011 1011 1001 1010 1100 1010 0000 0000

那么我觉得我可以把这个数分成两部分

0000 0000 0000 0000 0000 0010 0000 0000

0011 1011 1001 1010 1100 1000 0000 0000

也就是把这个数的第一个二进制位拆出来为a然后直接x-a=b,这样就可以了

那么我们是不是对每一个数其实都可以这样找到第一个二进制位为1的位数,然后取出来为a,

再用x-a为b

这看起来没啥问题,但是还是范围问题,为啥呢

先看1,这样子就是a=1,b=1-1=0,又出问题了,而且对于那些2的倍数,我们发现都会有问题

像4啊 a=4,b=0,那么怎么办,我们先把他们找出来,就像4就是100,那么我们就是用

101 001来异或,也就是1^x+1,当然这个也有对1有特例

#include <bits/stdc++.h>
#define N 1000001   //宏定义一个最大值
#define ll long long
using namespace std;    //命名空间
int a[N];

int an(int x) {
	for (int i = 0; i < log2(x); i++)
		if (x & (1 << i))
			return 1 << i;
	return 0;
}

int main() {
	int x, y, n, m, k, ans, sum = 0, t;
	cin >> t;
	while (t--) {
		cin >> x;
        if(x==1){puts("3 2");continue;}
		m = an(x);
		if (m)
			cout << m << ' ' << x - m << endl;
		else
			cout << x + 1 << ' ' <<1<< endl;
	}
}

C-solve3

枚举法,真的牛好吧

x=a^b

x^a=a^b^a=b

b=x^a

直接枚举a 使b在范围内就输出

#include <bits/stdc++.h>
#define N 1000001   //宏定义一个最大值
#define ll long long
using namespace std;    //命名空间
int an(int x) {
	for (int i = 1; i <=1e9; i++)
    if((x^i)<=1e9&&(x^i)>=1)return i;
    return 0;
}

int main() {
	int x, y, n, m, k, ans, sum = 0, t;
	cin >> t;
	while (t--) {
		cin >> x;
        cout<<an(x)<<' '<<(an(x)^x)<<endl;
	}
}

构造故事

直接说结论,最后找的一定是排序后连着的三个数

为什么呢,证明自己想

当然还是要说的,比如你先取三个数a,b,c不连续,但是可以组成三角形并且a>b>c

那么我们可不可以增加b呢?关键在于三角形成立条件  b变大了,a-b变小了,c肯定更容易满足了

那么我们可不可以增加c呢?那么是不是a-b变小,c还变大那就更满足了,所以最后就可以是三个连起来的,然后就遍历就好了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

void solve() {
	int n, x;
	cin >> n;
	vector<ll>a(n);
	for (int i = 0; i < n; i++) {
		cin >>a[i];
	}
	sort(a.begin(), a.end());
	ll ans = -1;
	for (int i = 0; i < n-2; i++)
		if (a[i] + a[i + 1] > a[i + 2])
			ans = a[i] + a[i + 1] + a[i + 2];
	cout << ans << endl;
}

int main() {
	int t;
	cin >> t;
	while (t--)
		solve();
}

E 约会故事

这个题挺麻烦,其他不说,就单说邀请时间的问题吧

1,因为就是24小时内,所以可以直接把小时换分钟吧,一天有1440分钟哦,然后开个数组存

因为最后给的是一个邀请时间,那就看邀请的这一分钟可不可以就好了

2,然后就是给的开始和结尾时间可能跨一天,那怎么办呢,我们首先可以分一下情况

如果在一天内,应该有开始时间小于结束时间,那么就直接从开始到结束就好

如过不在一天内,那么就是开始时间小于结束时间

我们画图可以看出来只要跨天的,一定会被24点切割成两部分(不然怎么叫跨天嘛

于是我们其实只要分开两部分就很正常了

最后就是begin==end这个就是全部时间都可以

其实最好先别考虑什么0~2点的限制,直接按一天就好,最后给邀请时间那里再判断就好

#include <bits/stdc++.h>
#define N 10005  //宏定义一个最大值
#define ll long long
using namespace std;    //命名空间
bool ok[2000];

int z(int a, int b) {
	return a * 60 + b;
}

int main() {
	int n, m, hh1, mm1, hh2, mm2, z1, z2, hh, mm, q, zz;
	cin >> n >> m;
	map<string, bool> a;
	string s;
	for (int i = 0; i < n; i++) {
		scanf("%d:%d %d:%d", &hh1, &mm1, &hh2, &mm2);
		int sum1, sum2;
		sum1 = z(hh1, mm1), sum2 = z(hh2, mm2);
		if (sum1 < sum2) {
			if (sum1 < 120) {
				for (int i = sum1; i <= sum2; i++)
					ok[i] = 1;
			}
		} else if (sum1 > sum2) {
			for (int i = sum1; i < 1440; i++)
				ok[i] = 1;
			for (int i = 0; i <= sum2; i++)
				ok[i] = 1;
		} else
			memset(ok, 0, sizeof(ok));
	}
	for (int i = 0; i < m; i++) {
		cin >> s;
		a[s] = 1;
	}
	cin >> q;
	while (q--) {
		scanf("%d:%d", &hh, &mm);
		zz = z(hh, mm);
		scanf("%d:%d %d:%d", &hh1, &mm1, &hh2, &mm2);
		cin >> s;
		if (zz < 120 && zz >= 0 && ok[zz]) {
			if (z(hh1, mm1) <= z(hh2, mm2) && a[s])
				cout << "Winner xqq" << endl;
			else
				cout << "Joker xqq" << endl;
		} else
			cout << "Loser xqq" << endl;
	}

	return 0;
}

附言:这题给哥们写笑了,但是留下了泪水,祝各位oier,acmer们不要当joker,和loser,但其实winner真的就是winner嘛,这次的winner或许只是下次joker的前兆罢了,约会这么阴间的还是速速分开吧。

还是不赌为赢,不谈为真winner

F 不是烤串故事

F-slove1(字符串哈希+二分)

这个一看字符串的处理,应该就理所想到了哈希,但是我还是要说,术业有专攻,方法二要比这个方法简单很多,也舒服很多,字符串哈希虽然非常通用,但是还是太容易被卡了!!!!!

这个题首先是要求这个翻转一部分后的字符串(s')和标准串的lcp最大值

那么我们可以先吧所给s直接翻转为s'求一个哈希,然后s自己一个哈希,标准串一个哈希

然后遍历从i=1~n的所有翻转情况,然后用二分去求每个情况下的lcp

具体如何二分呢?

二分lcp答案mid,如果mid<=i那mid仅在翻转串里,直接找s'和t对应子串哈希值是否相同即可,

如果mid>i那么就包括翻转串和原始串(未翻转),然后两个分开求即可,但是注意在合并翻转串和原始串时,需要对这个值进行进位操作,就像123和45要变成12345,一定不是直接相加,而是123*100+45=12345,一样的道理,然后每次比较是否相等即可

最后的最后,这道题会卡自然溢出,不可以直接ull 而是要额外定义一个 hash_mod

这里用了1e9+7和1997的一对数,有些数还会被卡,所以 牢底,不要相信哈希啊!!!

#include <bits/stdc++.h>
#define N 1000100  //宏定义一个最大值
#define ll long long
#define ull  long long
#define P 1997
const ll hash_mod = 1e9 + 7;
using namespace std;    //命名空间
ull h[3][N], p[N];
string s1, s2, s3;

ull ask(int s, int l, int r) {
	return ((h[s][r] - h[s][l - 1] * p[r - l + 1]) % hash_mod + hash_mod) % hash_mod;
}

void solve() {
	int n, ans1 = 0, ans2 = 1;
//     memset(h,0,sizeof(h));
	cin >> n;
	cin >> s1 >> s2;
	for(int i=0;i<n;i++)
	s3[i]=s1[n-i-1];
	cout<<s3<<endl;
//	s3 = s1;
//	reverse(s3.begin(), s3.end());
	for (int i = 1; i <= n; i++) {
		h[0][i] = (h[0][i - 1] * P % hash_mod + s1[i - 1]) % hash_mod ;
		h[2][i] = (h[2][i - 1] * P % hash_mod + s3[i - 1]) % hash_mod;
		h[1][i] = (h[1][i - 1] * P % hash_mod + s2[i - 1]) % hash_mod;
	}
	for (int i = 1; i <= n; i++) { //从第i个翻转
		int l = 1, r = n, m;
		ull tar = 0, now = 0;
		while (l <= r) {
			m = (l + r) >> 1;
			tar = h[1][m];
			if (m <= i) {
				now = ask(2, n - i + 1, n - i + m);
			} else {
				now = ask(0, i + 1, m);
				now = (now + ask(2, n - i + 1, n) * p[m - i] % hash_mod) % hash_mod;
			}
			if (tar == now) {
				l = m + 1;
				if (ans1 < m) {
					ans2 = i;
					ans1 = m;
				}
			} else
				r = m - 1;
		}
	}
	cout << ans1 << ' ' << ans2 << endl;
}

int main() {
	int T;
	std::ios::sync_with_stdio(0);
	std::cout.tie(0);
	std::cin.tie(0);
	cin >> T;
	p[0] = 1;
	for (int i = 1; i < N; i++)
		p[i] = p[i - 1] * P % hash_mod;
	while (T--) {
		solve();
	}
	return 0;
}

F-solve2(Z函数)

这个就是非常正规的一个写法了,看到lcp就要用这个Z函数(扩展kmp)

代码简洁 运行很快 不会被卡

其实思路和上一个差不多,还是枚举每个被翻转的情况,也是通过翻转s串得s'串进行预处理,然后先求t的z数组,然后求s’相对于t的p数组,也就是s'相对于t的lcp,然后还需要注意因为可能不止翻转的相等,在翻转相等的情况下,未反转的依旧相等,我们也需要加上。

这里是一个易错点,就是应该逆序处理这个t和s的相等关系,什么意思呢

S :aaabaa
T : aabbba

例如这两个,顺序得到  

    1 2 0 1 0 1

而逆序得到

    2 1 0 1 0 1

我们需要得到的是,这个点往后有多少是和t相等的,而不是之前,所以我们应该逆着求他之前的相等数组

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
	int n, cnt = 0;
	string s, t;
	cin >> n;
	cin >> s >> t;
	vector<int>ok(n + 1);
	for (int i = n-1; i>=0; i--) {
		if (s[i] == t[i])
			cnt++;
		else
			cnt = 0;
		ok[i + 1] = cnt;
	}
	reverse(s.begin(), s.end());
	s = ' ' + s, t = ' ' + t;
	vector<int>z(n + 1), p(n + 1);
	z[1] = n;
	for (int i = 2, l, r = 0; i <= n; i++) {
		if (i <= r)
			z[i] = min(r - i + 1, z[i - l + 1]);
		while (t[1 + z[i]] == t[i + z[i]])
			z[i]++;
		if (i + z[i] - 1 > r)
			l = i, r = i + z[i] - 1;
	}
	for (int i = 1, l, r = 0; i <= n; i++) {
		if (i <= r)
			p[i] = min(r - i + 1, z[i - l + 1]);
		while (1+p[i]<=n&&i+p[i]<=n&&t[1 + p[i]] == s[i + p[i]])
			p[i]++;
		if (i + p[i] - 1 > r)
			l = i, r = i + p[i] - 1;
	}
	int ans = 0, tmp, op=1;
	for (int i = 1; i <= n; i++) {
		tmp = p[n - i + 1];
		if (tmp == i)
			tmp += ok[i + 1];
		if (tmp > ans) {
			op = i;
			ans = tmp;
		}
	}
// 	for (int i = 1; i <= n; i++)
// 		cout << p[i] << ' ';
// 	cout << endl;
	cout << ans << ' ' << op << endl;
}

int main() {
	int t;
	cin >> t;
	while (t--)
		solve();
    return 0;
}

结语

这个题解写了我好久,但是大部分时间其实在干别的事,专心写起来,还是不慢的,而且最起码,写的时候,自己内心还是很充实的,继续保持呀,快要开学啦,这个暑假过得真快啊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值