3.8 CW 模拟赛 赛时记录
前言
不贪跟策略, 数据检验
冷静, 耐心, 放下
手好冷
看题
\(\rm{T1}\)
马周常数
-
先从已经确定的部分开始考虑
- 拆分序列法
- 一般从可以严格分成两部分来考虑
- 分成两个同性质的串加上一个构造 \((\)例如 \((X)Y\) 用于构造新的合法括号串\()\)
- 分成两个同性质的串, 递归分治
- 拆分序列法
-
先排除无效元素
-
\(\rm{dp}\)
- 先考虑最终答案的表达式 \((\)合法解的构造方案\()\) , 基础上进行 \(\rm{dp}\)
- 分段法
- 拿几个维度表示状态, 逐个转移
- 子序列类问题, 一维对应原串
\(\rm{T2}\)
妈的, 似有故人来
- 定义合法情况, 要求方案数
- 将条件数学化
- 往往利用 \(\rm{dp}\) , 结合约束处理当前方案数
- 关注构造方案 / 顺序
- 关注本质重复的转移是否存在
- 由合法情况导出的答案
- 先考虑最终答案的表达式 \((\)合法解的构造方案\()\) , 基础上进行 \(\rm{dp}\)
- 找到所有情况统一的构造方案
- 构造: 先推性质, 不行打表
- 贪心
- 往往应该把选择权留到后面去
- 超过一半类
- 构造一组都满足 \(a\) 条件的, 然后剩下的构造一个满足 \(a\) , 一个满足 \(b\) 的, 这样一定超过一半
- 贪心
- 构造: 先推性质, 不行打表
- 逆向思维
- 列出合法情况需要满足的表达式
- 在原序列中贪心选择最优情况
- 然后在基础上进行调整
\(\rm{T3}\)
这下何山真讲过了, 和上次一样忘记了
自己观察了
- 交换相邻元素性质
- 从 \(a\) 到 \(b\) , 交换 \(|a - b|\) 次
- 对于两个串的定位问题, 每个元素定位的花费就是关于其的逆序对个数
证明: 从大权值到小权值, 逐个固定位置 - 往往用固定之前的部分, 移动当前的部分来解决
- 如果交换有约束
- 可以认为一个数只能在固定区间内移动
- 可以看做对相对位置的约束
\(\rm{T4}\)
不会是 \(\rm{DS}\) 吧
做了这么多题, 还是知道自己肯定没时间去想 \(\rm{T3, T4}\) 正解的, 因此先开 \(\textrm{T1, T2}\) , \(\textrm{T3}\) 可以小磕一下拿下高档一点的, 然后顺手把 \(\rm{T4}\) 送的开了就功德圆满了
因此开打
\(\rm{T1}\)
-
先从已经确定的部分开始考虑
- 拆分序列法
- 一般从可以严格分成两部分来考虑
- 分成两个同性质的串加上一个构造 \((\)例如 \((X)Y\) 用于构造新的合法括号串\()\)
- 分成两个同性质的串, 递归分治
- 拆分序列法
-
先排除无效元素
-
\(\rm{dp}\)
- 先考虑最终答案的表达式 \((\)合法解的构造方案\()\) , 基础上进行 \(\rm{dp}\)
思路
先找对于一个 \(b\), \(\textrm{trans}(b)\) 的性质
大概找到了一个构造方法, 对于 \(1 \leq i \leq |b|\) , \(\textrm{trans}(b)_i\) 就是 \(\geq b_i\) 的数中第一个没有被使用的
因为要维护, 所以形式化一下
最初 \(p_i \gets i\) , 每次对于 \(b_i\) , 放置 \(p_{b_i}\) , \(p_{b_i} \gets p_{b_i} + 1\) 然后对于所有 \(p_k = p_{b_i} - 1, p_k \gets p_k + 1\)
因此知道了 \(\textrm{trans}(b)_1\) , 我们可以推出 \(b_1 = \textrm{trans}(b)_1, p_{b_1} = \textrm{trans}(b)_1\) , 在接下来的放置中, \(p_{b_1} \gets p_{b_1} + 1\) , 对于所有 \(p_k = p_{b_1} - 1, p_k \gets p_k + 1\)
不对不对, 换思路, 这个一点不好维护
发现一个更好的性质, 对于 \(b\) 一个位置的填法, 取决于之前的空窗期位置
等一下等一下, 好像对了
现在问题就是怎么高效维护这个东西, 打算死磕一下
似乎可以二分, 做到 \(\mathcal{O} (n \log n)\) , 做完了做完了
慢, 为什么想歪这么多, 还好拐回来了
为什么我想不到怎么 \(\mathcal{O} (1)\) 求区间和
实现
本来想写线段树上二分的, 但是想起来之前被树状数组双 \(\log\) 薄纱的经历决定写双 \(\log\)
傻逼 \(\textrm{fastio}\) , 能不能注明是 \(\textrm{Lunix}\) 下的
代码
#include <bits/stdc++.h>
struct fio{
#define isdigit(x) (x >= '0' && x <= '9')
char buf[1 << 20], *p1, pbuf[1 << 20], *p2, *pp;
fio() : p1(buf), p2(buf), pp(pbuf){}
~fio(){fwrite(pbuf, 1, pp - pbuf, stdout);}
inline char gc(){return getchar(); }
inline void pc(const char &c){if (pp - pbuf == 1 << 20) fwrite(pbuf, 1, 1 << 20, stdout), pp = pbuf;*pp ++ = c;}
inline bool blank(char ch){return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';}
template <class T> inline void read(T &x){
long double tmp = 1;bool sign = 0;char ch = gc();x = 0;
for (;!isdigit(ch);ch = gc()) if (ch == '-') sign = 1;
for (;isdigit(ch);ch = gc()) x = x * 10 + (ch - '0');
if (ch == '.') for (ch = gc();isdigit(ch);ch = gc()) tmp /= 10.0, x += tmp * (ch - '0');
if (sign) x = -x;
}
inline void read(char *s){
char ch = gc();for (;blank(ch);ch = gc());
for (;!blank(ch);ch = gc()) *s ++ = ch;*s = 0;
}
inline void read(char &c){for (c = gc();blank(c);c = gc());}
template <class T> inline void write(T x){
if (x < 0) x = -x, putchar('-');int sta[30];int top = 0;
do sta[top ++] = x % 10, x /= 10;while (x);
while (top) putchar(sta[-- top] + '0');
}
}io;
#define int long long
#define lowbit(x) (x & -(x))
const int MAXN = 2e6 + 20;
const int MOD = 1e9 + 7;
namespace calc {
int add(int a, int b) { return a + b >= MOD ? a + b - MOD : a + b; }
int mul(int a, int b) { return (a * b) % MOD; }
int mus(int a, int b) { return a - b < 0 ? a - b + MOD : a - b; }
void addon(int &a, int b) { a = add(a, b); }
void mulon(int &a, int b) { a = mul(a, b); }
} using namespace calc;
int n, m = 0;
int a[MAXN];
struct BIT {
int val[MAXN << 1];
BIT() { memset(val, 0, sizeof val); }
void modify(int p, int d) { while (p <= m) val[p] += d, p += lowbit(p); }
int query(int p) { int ans = 0; while (p > 0) ans += val[p], p -= lowbit(p); return ans; }
} bit;
class bsearcher {
private:
public:
bool check(int x, int p) { return bit.query(p) - bit.query(x - 1) == p - x + 1; }
int solve(int p) {
int left = 1, right = p + 1, ans = 0;
while (left < right) {
int mid = (left + right) >> 1;
if (check(mid, p)) ans = mid, right = mid;
else left = mid + 1;
}
return ans;
}
} src;
signed main() {
// freopen("ex_trans4.in", "r", stdin);
io.read(n);
for (int i = 1; i <= n; i++) io.read(a[i]), m = std::max(m, a[i]);
int ans = 1;
for (int i = 1; i <= n; i++) {
bit.modify(a[i], 1);
int p = src.solve(a[i]);
mulon(ans, a[i] - p + 1);
}
io.write(ans);
return 0;
}
\(\rm{T2}\)
发现以后实现还是单开, 不然容易挂
框架确实还是需要的
神经, 吃个送分题这么费劲
剩下的应该就是能拿多少拿多少了, 按照时间分配来, 不贪
- 定义合法情况, 要求方案数
- 将条件数学化
- 往往利用 \(\rm{dp}\) , 结合约束处理当前方案数
- 关注构造方案 / 顺序
- 由合法情况导出的答案
- 先考虑最终答案的表达式 \((\)合法解的构造方案\()\) , 基础上进行 \(\rm{dp}\)
- 关注本质重复的转移是否存在
- 找到所有情况统一的构造方案
- 构造: 先推性质, 不行打表
- 贪心
- 往往应该把选择权留到后面去
- 贪心
- 构造: 先推性质, 不行打表
- 先从已经确定的部分开始考虑
- 拆分序列法
- 一般从可以严格分成两部分来考虑
- 分成两个同性质的串加上一个构造 \((\)例如 \((X)Y\) 用于构造新的合法括号串\()\)
- 分成两个同性质的串, 递归分治
- 拆分序列法
- 先排除无效元素
思路
题意
一个合法序列 定义为
- 不存在和相等的前后缀
给定 , 求有多少种合法序列
发现 \(n\) 很小
转化到值域上面去
我们把 \(\sum a_i\) 拆成一个个 \(1\) , 转化成划线的问题, 好像也不行
时间到了, 只能打 \(10\) 走
\(\rm{T3}\)
那我不爆了, 真服了, 啥都不会
新增贪心公式, 然后跟着策略走
思路
何山大抵是讲过这个的, 我大概也是忘完了的
哎哎哎哥们好像送了 \(40\)
谢谢谢谢
没时间了, 打 \(40\)

浙公网安备 33010602011771号