NOIP2023-div2模拟赛24
2023.10.23
期望得分:
实际得分:
A. 公园
考虑从小到大枚举
时间复杂度
B. 括号
沿用 csp-s 2023 的套路,计算以每一个 )
结尾的合法括号序列的方案数。对于每一个 )
,它最多只有一个与它配对的 (
,可以通过栈找到,所以每个能匹配的 )
均可以产生
如果要产生更多的贡献,只有可能在他前面接上一个合法的序列,且能接几个就能产生多少的贡献。我们定义这样配对完剩余左括号数量相同的前缀是同一层的。两个同一层的前缀能接起来当且仅当中间不存在层数少于当前层数的。因为字符串是一连串的左右括号交替出现。所以同层的情况只有可能出现在左括号用完了一整段或右括号用完了一整段。用栈维护层数的同时维护层数出现的次数,然后每次统计层数贡献时要将层数大于当前层数的弹出。
时间复杂度
code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <unordered_map>
using namespace std;
const int kmax = 2e7 + 3;
int c[kmax];
int p[2];
int n, X, Y, Z;
int stk[kmax], tp;
unsigned long long res;
long long stc[kmax], ct[kmax], tc;
long long cur;
void Init() {
cin >> n >> X >> Y >> Z >> p[0] >> p[1] >> c[0] >> c[1];
for(int i = 2; i < n; i++) {
c[i] = (1ll * c[i - 1] * X + 1ll * c[i - 2] * Y + 1ll * Z) % p[i & 1] + 1;
}
}
int main() {
freopen("brackets.in", "r", stdin);
freopen("brackets.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
Init();
// for(int i = 0; i < n; i++) {
// cout << c[i] << ' ';
// }
// cin >> n;
// for(int i = 0; i < n; i++) {
// cin >> c[i];
// }
for(int i = 0; i < n; i++) {
if(i & 1) {
for(; tp && c[i]; ) {
if(c[i] >= stk[tp]) {
// cout << "get 1\n";
cur -= stk[tp];
res += stk[tp];
// cout << res << '\n';
c[i] -= stk[tp];
for(tp--; tc && stc[tc] > cur; ct[tc--] = 0) {
}
// cout << cur << ' ' << ct[cur] << '\n';
if(tc && stc[tc] == cur) {
res += ct[tc];
ct[tc]++;
} else {
stc[++tc] = cur;
ct[tc] = 1;
}
// cout << res << ' ' << cur << "***" << ' ' << ct[cur] << '\n';
} else {
// cout << "get 2\n";
cur -= c[i];
res += c[i];
// cout << res << '\n';
stk[tp] -= c[i];
c[i] = 0;
for(; tc && stc[tc] > cur; ct[tc--] = 0) {
// cout << "ERROR" << cur << ' ' << stc[tc] << '\n';
}
// cout << cur << ' ' << ct[cur] << '\n';
if(tc && stc[tc] == cur) {
res += ct[tc];
ct[tc]++;
} else {
stc[++tc] = cur;
ct[tc] = 1;
}
// cout << res << ' ' << cur << "***" << ' ' << ct[cur] << '\n';
}
}
if(c[i]) {
for(; tc; ct[tc--] = 0) {
}
}
} else {
stk[++tp] = c[i];
cur += c[i];
}
// cout << i << ' ' << res << ' ' << cur << '\n';
}
cout << res << '\n';
return 0;
}
C. 学校
考虑朴素的dp。定义
观察到
或和为
将式子拆开,相当于要求
code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int kmax = 4005;
const int kmaxM = 12;
const int Mod = 998244353;
int n, m, s, a[kmax];
long long f[kmax][kmax], res;
long long g[1 << kmaxM], tot;
int main() {
freopen("school.in", "r", stdin);
freopen("school.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m >> s;
for(int i = 1; i <= n; i++) {
cin >> a[i];
f[i][0] = 1;
}
for(int i = 1; i <= n; i++) {
tot = 0;
for(int j = 0; j < i; j++) tot = (tot + f[i][j]) % Mod;
res = (res + tot) % Mod;
for(int j = i + 1; j <= n; j++) {
f[j][i] = (tot - g[s ^ a[i] ^ a[j]] + Mod) % Mod;
}
for(int j = 1; j < i; j++) {
g[a[i] ^ a[j]] = (g[a[i] ^ a[j]] + f[i][j]) % Mod;
}
}
cout << res << '\n';
return 0;
}
D. 运算
不难发现,我们不在乎最后乘出来的数值是多少,只在乎它对
但是这样的边太多了,考虑压缩边的数量。将每一条边挂在区间的左端点上,每次转移时就往左跳区间长度个位置就行了。这样只有
但这样每个点会遍历多次,产生冗余。可以考虑并查集优化,将遍历过的点并在它后面的点上。最短路的边权都为
code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int kmax = 1e6 + 3;
int t;
int n, d, l, r, m;
int dis[kmax];
bool b[kmax];
int f[kmax];
vector<int> e[kmax];
int F(int x) {
return f[x] == x ? x : f[x] = F(f[x]);
}
void Dijkstra() {
queue<int> q;
for(int i = 0; i <= n; i++) {
dis[i] = 1e9;
f[i] = i;
}
for(int z = F((n - r) % n); z <= (n - l) % n; z = F(z)) {
f[z] = z + 1, dis[z] = 0;
q.push(z);
}
for(; !q.empty(); q.pop()) {
int x = q.front();
for(int y : e[x]) {
if((y - r + n) % n <= (y - l + n) % n) {
// cout << (y - r + n) % n << ' ' << (y - l + n) % n << '\n';
for(int z = F((y - r + n) % n); z <= (y - l + n) % n; z = F(z)) {
// cout << "z = " << z << '\n';
f[z] = z + 1, dis[z] = dis[x] + 1;
q.push(z);
}
} else {
for(int z = F((y - r + n) % n); z < n; z = F(z)) {
f[z] = z + 1, dis[z] = dis[x] + 1;
q.push(z);
}
for(int z = F(0); z <= (y - l + n) % n; z = F(z)) {
f[z] = z + 1, dis[z] = dis[x] + 1;
q.push(z);
}
}
}
}
}
void Solve() {
cin >> n >> d >> l >> r >> m;
for(int i = 0; i < n; i++) {
e[i].clear();
}
for(int i = 0; i < n; i++) {
int x = 1ll * i * d % n;
// cout << (x + l) % n << "***\n";
e[x].push_back(i);
}
Dijkstra();
// for(int i = 0; i < n; i++) {
// cout << i << ' ' << dis[i] << '\n';
// }
for(int i = 1, x; i <= m; i++) {
cin >> x;
cout << (dis[x] == 1e9 ? -1 : dis[x]) << '\n';
}
}
int main() {
freopen("calculator.in", "r", stdin);
freopen("calculator.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> t;
while(t--) {
Solve();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效