题目:2024.10.17 题目

语言

有一种新的语言,这种语言有 个单次分别对应 。其中,第 个字符词性为第 类。第 类分别为:A, N, A/N, V, A/V, N/V, A/N/V。一个合法的句子为名词词组+动词+名词词组。名词词组可以由一个名词(N)、一个形容词(V)加一个名词词组、两个名词词组组成。现在给定字符序列 ,判断 能否组成一个合法的句子。


签到题。根据题意,并结合学习英语的经验,不难发现只有 XX…N V XX…N 的形式才能组成一个合法的句子,其中 X 既可以是形容词 A 也可以是名词 N。因此我们枚举 V 的位置,然后对于两边做前/后缀逻辑与并特判即可。

#include <fstream>
#include <string>
 
using namespace std;
 
const int kMaxN = 1e5 + 5;
 
ifstream cin("language.in");
ofstream cout("language.out");
 
int w[kMaxN], pre[kMaxN], suf[kMaxN], t, n, ans;
string s;
 
int main() {
  cin.tie(0)->sync_with_stdio(0);
  for (cin >> t; t; t--) {
    for (int i = 'a'; i <= 'z'; i++) {
      cin >> w[i];
    }
    cin >> s, ans = 0;
    n = s.size();
    s = ' ' + s;
    pre[0] = 1;
    for (int i = 1; i <= n; i++) {
      pre[i] = pre[i - 1] && (w[s[i]] != 4);
    }
    suf[n + 1] = 1;
    for (int i = n; i >= 1; i--) {
      suf[i] = suf[i + 1] && (w[s[i]] != 4);
    }
    for (int i = 2; i < n && !ans; i++) {
      ans = w[s[i]] >= 4 &&                      // 自己是动词
            w[s[i - 1]] != 1 && w[s[n]] != 1 &&  // 左边和右边最后一个是名词
            pre[i - 1] && suf[i + 1];            // 名词词组
    }
    cout << (ans ? "Yes" : "No") << '\n';
  }
  return 0;
}
 

色球

种颜色的小球,每种颜色有无限个。还有 个球桶,每个球桶能够容下无限多的小球。一开始的球桶都是空的,有 次操作,每次操作都是以下三种的任意一种:

  • 个颜色为 的彩色小球放到第 个桶的最上面;
  • 把嘴上面的 个小球从第 个桶里面拿出来,并输出最后拿出来的球的颜色。保证第 个桶里面的球数不小于 个;
  • 把第 个桶里面的小球依次从顶部拿出并放入第 个桶内。

由于颜色数量最多只有 种,且每种颜色的球数很多,不难考虑以颜色为单位进行处理。可以想到 的暴力:对于每一个桶都开一个 vector,加入新的球 ,弹出球总共 均摊 ,只有移动球的时间复杂度会达到 ,这将使我们超时。

收到启发式合并的影响,我们不如思考一下对于第三种操作怎么使用启发式合并。模拟可以得到,将小的合并到大的顺序会反转,因此我们还需要额外的变量来记录合并后是否反转,那么合并又要进行四种方式的特判了,过于复杂。

由于时间复杂度的瓶颈主要在第三种操作上面,因此考虑如何优化第三种操作的时间复杂度就成了刚需。在脑海中经过短暂思考,容易想到有一种冷门数据结构叫做「链表」。链表同样支持一二操作的 时间复杂度,因为只需要进行头尾的插删,而第三种操作实际上就是将链表头连接到目标链表的链表头上。至此,本题便结束了。

注意:本题代码难度较高,且特别需要注意细节。好渴鹅就是因为一个小错误调试了两个小时半。(代码是删掉了文明用语的文明绿色版,调试给我调红温了)

#include <fstream>
#include <string>
 
using namespace std;
using LL = long long;
 
const LL kMaxN = 2e5 + 5;
 
ifstream cin("ball.in");
ofstream cout("ball.out");
 
struct Node {
  LL color, count, nxt, prv;
} lst[kMaxN];
 
LL head[kMaxN], tail[kMaxN], n, m, c;
string s;
 
void fuckedAdd(LL x, LL y, LL z) {
  lst[++c].color = y, lst[c].count = x;
  lst[c].prv = tail[z];
  if (tail[z]) {
    (lst[tail[z]].prv ? lst[tail[z]].nxt : lst[tail[z]].prv) = c;
  } else {
    head[z] = c;
  }
  tail[z] = c;
}
 
void fuckedDelete(LL shit) {
  LL p;
  if (lst[tail[shit]].prv) {
    p = lst[tail[shit]].prv;
  } else {
    p = lst[tail[shit]].nxt;
  }
  if (p)  {
    (lst[p].prv == tail[shit] && (lst[p].prv = 0, 1)) || (lst[p].nxt = 0);
  }
  tail[shit] = p;
}
 
void nodeUGetMarriedWithFuckedNodeVWhoIsYourMother(LL x, LL y) {
  if (head[y]) {
    lst[tail[x]].prv ? lst[tail[x]].nxt = tail[y] : lst[tail[x]].prv = tail[y];
    lst[tail[y]].prv ? lst[tail[y]].nxt = tail[x] : lst[tail[y]].prv = tail[x];
  } else {
    head[y] = tail[x];
  }
  tail[y] = head[x];
  head[x] = tail[x] = 0;
}
 
int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> n >> m;
  for (LL x, y, z; m; m--) {
    cin >> s >> x >> y;
    if (s == "push") {
      cin >> z;
      fuckedAdd(x, y, z);
    } else if (s == "pop") {
      while (x > lst[tail[y]].count) {
        x -= lst[tail[y]].count;
        fuckedDelete(y);
      }
      lst[tail[y]].count -= x;
      cout << lst[tail[y]].color << '\n';
    } else if (s == "put") {
      if (!tail[x]) {
        continue;
      }
      nodeUGetMarriedWithFuckedNodeVWhoIsYourMother(x, y);
    } else {
      cout << "Fuck your mother!" << endl;
    }
  }
  return 0;
}

斐波

。假设 为可重集 ,则 定义为 。有一个数组 ,会进行 次操作。每次操作都是以下两种操作的一种:

  • 变为
  • 计算

对于操作 ,答案对 取模。


刚调完第二题,没时间写了,文件都没有创建, 分。出题人,我爱你。

偶数

对于一个正整数(去掉前导 ),如果它在十进制下位数为偶数且前一半与后一半一致,则称这个数为“偶数”。对于一个“偶数”,牛牛可以在这个数后面继续添加数字,使它成为一个新的偶数。牛牛总是添加最少的数字获得新的“偶数”,因此添加的方式唯一。

牛牛会对于一个偶数 ,一直产生新的偶数,直到偶数的位数超过给定的正整数 。牛牛会多次询问你这个偶数的第 为所组成的整数摸 的值是多少。 组数据。


刚调完第二题,没时间写了,而且题目都没有完全读懂,文件都没有创建, 分。出题人,我爱你。

总结

  • 排名
  • 比赛分数
  • 情况:相比 2024.10.14 模拟赛,没区别;
  • 问题:调不出就红温。