牛客暑假多校 2023 第八场
写在前面
比赛地址:https://ac.nowcoder.com/acm/contest/57362
我是一坨屎。
以下按照个人向难度排序。
A
签到,直接模拟。
//
/*
By:Luckyblock
*/
#include <map>
#include <cmath>
#include <cstdio>
#include <string>
#include <cctype>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
//=============================================================
int n, cnt;
std::map <std::string, int> yes;
std::vector <std::string> ans;
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
n = read();
for (int i = 1; i <= n; ++ i) {
int k = read();
for (int j = 1; j <= k; ++ j) {
std::string s;
std::cin >> s;
if (yes.count(s)) yes[s] = yes[s] + 1;
else {
yes[s] = 1;
ans.push_back(s);
}
if (yes[s] == n) ++ cnt;
}
}
printf("%d\n", cnt);
std::sort(ans.begin(), ans.end());
for (int i = 0, sz = ans.size(); i < sz; ++ i) {
std::string s = ans[i];
if (yes[s] == n) std::cout << s << "\n";
}
return 0;
}
I
考虑把 \(p, q\) 看成通配符,枚举 \(p,q\) 的长度,发现此时前后两半的形态就可以确定了。如果这两段中对应位置上均为通配符则贡献为 26,如果一个为通配符则贡献为 1,否则判断两个位置是否相等,如果相等则贡献为 1,否则为 0。此次枚举总贡献即为所有位置贡献之积。
发现上述三种需要判断的情况对应的区间都是可以确定的,仅需关注均不为通配符的段是否相同,大力讨论哈希即可。总复杂度 \(O(m)\) 级别,就是有一点点难写。
其实这题在这五道题里是过得最少的,但是不需要脑子就能写,所以在这个位置。
赛时有个边界推错了真是想紫砂了。
//
/*
By:Luckyblock
*/
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define LL long long
const int kN = 2e6 + 10;
const LL base = 31;
const LL p1 = 1e9 + 7;
const LL p2 = 1e9 + 9;
const LL mod = 998244353;
//=============================================================
int m, lens, lent;
char s[kN], t[kN];
LL ans, facb1[kN], facb2[kN], hs1[kN], hs2[kN], ht1[kN], ht2[kN];
LL fac26[kN];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Init() {
m = read();
scanf("%s", s + 1); lens = strlen(s + 1);
scanf("%s", t + 1); lent = strlen(t + 1);
facb1[0] = facb2[0] = fac26[0] = 1ll;
for (int i = 1; i < kN; ++ i) fac26[i] = fac26[i - 1] * 26ll % mod;
for (int i = 1, len = std::max(lens, lent); i <= len; ++ i) {
facb1[i] = facb1[i - 1] * base % p1;
facb2[i] = facb2[i - 1] * base % p2;
}
for (int i = 1; i <= lens; ++ i) {
hs1[i] = (base * hs1[i - 1] + s[i]) % p1;
hs2[i] = (base * hs2[i - 1] + s[i]) % p2;
}
for (int i = 1; i <= lent; ++ i) {
ht1[i] = (base * ht1[i - 1] + t[i]) % p1;
ht2[i] = (base * ht2[i - 1] + t[i]) % p2;
}
}
LL Hash_s_1(int L_, int R_) {
return (hs1[R_] - facb1[R_ - L_ + 1] * hs1[L_ - 1] % p1 + p1) % p1;
}
LL Hash_s_2(int L_, int R_) {
return (hs2[R_] - facb2[R_ - L_ + 1] * hs2[L_ - 1] % p2 + p2) % p2;
}
LL Hash_t_1(int L_, int R_) {
return (ht1[R_] - facb1[R_ - L_ + 1] * ht1[L_ - 1] % p1 + p1) % p1;
}
LL Hash_t_2(int L_, int R_) {
return (ht2[R_] - facb2[R_ - L_ + 1] * ht2[L_ - 1] % p2 + p2) % p2;
}
void Solve(LL delta_) {
LL lenhalf = (lens + lent + 2ll * delta_) / 2ll;
if (delta_ + lens + delta_ <= lenhalf) {
int begint = lenhalf - (delta_ + lens + delta_);
LL nows1 = Hash_s_1(1, lens), nows2 = Hash_s_2(1, lens);
LL nowt1 = Hash_t_1(begint + delta_ + 1, begint + delta_ + 1 + lens - 1);
LL nowt2 = Hash_t_2(begint + delta_ + 1, begint + delta_ + 1 + lens - 1);
if (nows1 != nowt1 || nows2 != nowt2) return ;
nows1 = Hash_t_1(1, begint), nows2 = Hash_t_2(1, begint);
nowt1 = Hash_t_1(lent - begint + 1, lent);
nowt2 = Hash_t_2(lent - begint + 1, lent);
if (nows1 != nowt1 || nows2 != nowt2) return ;
ans = 1ll;
// } else if (delta_ + lens + delta_ == lenhalf) {
} else if (delta_ + lens <= lenhalf) {
int r = lenhalf - (delta_ + lens);
LL nows1 = Hash_s_1(1, lens), nows2 = Hash_s_2(1, lens);
LL nowt1 = Hash_t_1(lent - r - lens + 1, lent - r);
LL nowt2 = Hash_t_2(lent - r - lens + 1, lent - r);
if (nows1 != nowt1 || nows2 != nowt2) return ;
// } else if (delta_ + lens == lenhalf) {
ans = fac26[delta_ - r];
} else {
int r = delta_ + lens - lenhalf;
LL nows1 = Hash_s_1(lens - r - lent + 1, lens - r);
LL nows2 = Hash_s_2(lens - r - lent + 1, lens - r);
LL nowt1 = Hash_t_1(1, lent);
LL nowt2 = Hash_t_2(1, lent);
if (nows1 != nowt1 || nows2 != nowt2) return ;
if (r - delta_ >= 1) {
nows1 = Hash_s_1(1, r - delta_);
nows2 = Hash_s_2(1, r - delta_);
nowt1 = Hash_s_1(lens - (r - delta_) + 1, lens);
nowt2 = Hash_s_2(lens - (r - delta_) + 1, lens);
if (nows1 != nowt1 || nows2 != nowt2) return ;
ans = 1ll;
} else {
ans = fac26[delta_ - r];
}
}
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
// freopen("std.txt", "w", stdout);
Init();
for (int i = 1; i <= m; ++ i) {
ans = 0;
if ((lens + lent) % 2 == 0ll) Solve(1ll * i);
printf("%lld ", ans);
}
printf("\n");
return 0;
}
H
显然的性质:
- 若区间 \([L, R]\) 合法,则 \(a_L = 1\),且 \([L, x] (L\le x \le R)\) 均合法;
- 若区间 \([L, x]\) 合法,\([x+1,R]\) 合法,则 \([L, R]\) 合法。
- 对于合法区间 \([L, R]\) 内的元素 \(a_i (L\le i\le R)\),在区间中其左侧一定有一个 \(a_i -1\) 与之配对,则如果 \([L, R+1]\) 不合法,则 \(a_R\) 的左侧没有 \(a_{R} - 1\) 与之配对。
一个显然的想法是考虑枚举 \(a_L=1\) 的左端点 \(L\),求得右侧第一个不合法的右端点 \(R\),则以 \(L\) 为左端点的合法区间的贡献为 \(R-L\)。
考虑倒序枚举左端点 \(L\),过程中用栈维护右侧第一个不合法的右端点 \(R\)(即 \(a_R\) 未与左侧配对)。如果 \(a_L \not= 1\) 则将位置 \(L\) 入栈,否则尝试进行匹配,检查栈顶第一个元素 \(a_r\) 的左侧是否有已经与左侧配对并且未与右侧配对的 \(a_r-1\),如果有则将该 \(a_r\) 设为未与右侧配对并出栈……重复上述过程直至无法进行。
可以用位置单调递减的单调栈,维护与左侧配对并且未与右侧配对的 \(a_r-1\) 的位置,于是对每一种权值都维护一个单调栈即可。
每个位置仅会入栈并出栈一次,总复杂度 \(O(n)\) 级别。
对每种权值维护单调栈时,直接用 STL 的 std::stack 会 MLE,但是改成 std::vector 就过了,理解不能。
//
/*
By:Luckyblock
*/
#include <stack>
#include <cmath>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <algorithm>
#define LL long long
const int kN = 1e6 + 10;
//=============================================================
int n, a[kN];
std::stack <int> st;
std::vector <int> s[kN];
LL ans;
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
n = read();
for (int i = 1; i <= n; ++ i) a[i] = read();
st.push(n + 1);
for (int l = n; l; -- l) {
if (a[l] == 1) {
s[1].push_back(l);
while (st.top() <= n && !s[a[st.top()] - 1].empty() &&
s[a[st.top()] - 1].back() < st.top()) {
s[a[st.top()] - 1].pop_back();
s[a[st.top()]].push_back(st.top());
st.pop();
}
ans += 1ll * st.top() - l;
} else {
st.push(l);
}
}
printf("%lld\n", ans);
return 0;
}
/*
5
1 2 1 3 2
*/
J
走了很多弯路……
发现和为奇质数似乎没有通用的构造方法,于是尽量考虑差能否为奇质数。
发现仅考虑相邻元素的影响,一个显然的想法是将所有数分成差为质数的若干段,比如:1 4 7 10、2 5 8 11、3 6 9 12,于是仅需考虑交界处能否合法。
基于这个思路,发现令段内差值为 5 时即可通过如下排列方法构造出合法方案:
5...5m 5m-3 ... 2 1 ... 5m-4 5m-1... 4 3...5m-2。
讨论下 \(n\) 对 5 取余的余数即可。
Code by nebulyu:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ffor(i,a,b) for(int i=a;i<=b;++i)
#define rfor(i,a,b) for(ll i=a;i>=b;--i)
using namespace std;
using ll=long long;
const int N=1e6+5;
void solve(){
int n;cin>>n;
int m=n/5;
// 5 5m|5m-3 2|1 5m-4|5m-1 4|3 5m-2
if(n%5==0){
for(int i=5;i<=5*m;i+=5)cout<<i<<" ";
for(int i=5*m-3;i>=2;i-=5)cout<<i<<" ";
for(int i=1;i<=5*m-4;i+=5)cout<<i<<" ";
for(int i=5*m-1;i>=4;i-=5)cout<<i<<" ";
for(int i=3;i<=5*m-2;i+=5)cout<<i<<" ";
cout<<"\n";return ;
}
// 5 5m|5m-3 2|3 5m-2|5m+1 1|4 5m-1
if(n%5==1){
for(int i=5;i<=5*m;i+=5)cout<<i<<" ";
for(int i=5*m-3;i>=2;i-=5)cout<<i<<" ";
for(int i=3;i<=5*m;i+=5)cout<<i<<" ";
for(int i=5*m+1;i>=1;i-=5)cout<<i<<" ";
for(int i=4;i<=5*m;i+=5)cout<<i<<" ";
cout<<"\n";return ;
}
// 5m 5|2 5m+2|5m-1 4|3 5m-2|5m+1 1
if(n%5==2){
for(int i=5*m;i>=5;i-=5)cout<<i<<" ";
for(int i=2;i<=5*m+2;i+=5)cout<<i<<" ";
for(int i=5*m-1;i>=4;i-=5)cout<<i<<" ";
for(int i=3;i<=5*m-2;i+=5)cout<<i<<" ";
for(int i=5*m+1;i>=1;i-=5)cout<<i<<" ";
cout<<"\n";return ;
}
// 5 5m|5m+3 3|4 5m-1|5m+2 2|1 5m+1
if(n%5==3){
for(int i=5;i<=5*m;i+=5)cout<<i<<" ";
for(int i=5*m+3;i>=3;i-=5)cout<<i<<" ";
for(int i=4;i<=5*m-1;i+=5)cout<<i<<" ";
for(int i=5*m+2;i>=2;i-=5)cout<<i<<" ";
for(int i=1;i<=5*m+1;i+=5)cout<<i<<" ";
cout<<"\n";return ;
}
// 1 5m+1|5m+4 4|3 5m+3|5m 5|2 5m+2
if(n%5==4){
for(int i=1;i<=5*m+1;i+=5)cout<<i<<" ";
for(int i=5*m+4;i>=4;i-=5)cout<<i<<" ";
for(int i=3;i<=5*m+3;i+=5)cout<<i<<" ";
for(int i=5*m;i>=5;i-=5)cout<<i<<" ";
for(int i=2;i<=5*m+2;i+=5)cout<<i<<" ";
cout<<"\n";return ;
}
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T;cin>>T;while(T--)solve();
return 0;
}
写在最后
学到了什么:
std::stack垃圾。

浙公网安备 33010602011771号