牛客暑假多校 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 102 5 8 113 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 垃圾。
posted @ 2023-08-12 15:34  Luckyblock  阅读(68)  评论(0)    收藏  举报