前言
还是要写题解啊,孩子们,真不是为了别人,是为了未来的你自己啊,今天开始立志每场比赛都写题解,记录下那巧思,真的很重要!!!(我觉得)
正文
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;
}
}
D 构造故事
直接说结论,最后找的一定是排序后连着的三个数
为什么呢,证明自己想
当然还是要说的,比如你先取三个数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;
}
结语
这个题解写了我好久,但是大部分时间其实在干别的事,专心写起来,还是不慢的,而且最起码,写的时候,自己内心还是很充实的,继续保持呀,快要开学啦,这个暑假过得真快啊