总结
集合
考虑枚举子集和,统计有多少个子集的和为当前枚举的子集和,然后我们记个结论:\(x^y=x^{y \mod (p - 1)}\),然后就过了
P3488
一眼二分图(网络流启动),但是考虑到图很大,所以我们考虑直接判断是否是二分图,考虑一个区间,如果总数比这个区间所能承载的人都要大,那么肯定会寄,所以用线段树维护每个区间的最大字段和
连通块
考虑没有限制,那么就是一个树上的 \(dp\),加上限制后,我们考虑,每加一个子树相当于加一个dfn序,所以我们可以记录当前dfn最后一个位置,那么如果要加一个子树,那么就可以从限制点的位置-1的地方转移过来,但是我们只要处理限制点,所以我们可以离散化,然后只处理限制点
跳棋
考虑整个序列是如何变动的,我们观察到,当两个 1 组在一起时,是可以一直动的,因为碰到一个 1 时,可以从跳跃换成接替,那么我们便可以将 11 压在一起变成 2,那么数组终将会有四种元素:\(0,1,2,?\)
考虑没有问号的情况,那么整个问题变成了 2 的放置,我们发现 1 没有贡献,所以我们只用看 0 和 2 的个数,那么变成了在 0 和 2 的个数和中选取 2 的个数个位置放置 2,那么我们可以用 \(dp\) 的方式来给 \(?\) 分配符号,考虑到答案是与 0 的个数, 和 2 的个数有关,所以我们记录下 0 的个数和 2 的个数,那么我们初始认为状态时 \(f_{ijk}\) 表示前 \(i\) 个,有 \(j\) 个 2 \(k\) 个 0
但是我们要思考 \(j\) 和 \(k\) 要如何统计,那么统计我们需要知道上一位是什么,但是考虑到 \(2\) 的统计是当前面有偶数个 \(1\) 的时候才能凑齐 2,所以我们要加一个前面有奇数偶数个 1 所以状态变成 \(f_{ijk\ 0/1\ 0/1}\)
考虑转移,那么从当前是什么来考虑,分几种当前是什么的情况,然后注意当只有当前面有偶数个 1 的时候才能统计 \(2\) 的个数
最后对于目标状态,由于已经不存在 ? 所以可以直接用组合数做,然后要滚动数组
总结
由于思路错误,导致大部分时间在想 Dinic 如何解决删人问题,所以其它题基本没有想(不过我Dinic写对了!)。
114
我们手玩的知,选取方式如 \(AABABABAB\) 的形式,而 0 的存在可以将最坏情况交给对面,所以 0 起调整作用,然后在 1 和 2 中选择一个进行判断,如果存在一个数可以使得先手必胜,那么先手必胜
514
考虑第一个 \(a_i < i\) 的位置,假设这个位置是 \(i\)
根据题意我们得知:\(a_1\) 可以填的数种类为 \(n - 1 + 1\),\(a_2\) 可以填的数种类为 \(n - 2 + 1\),于是我们知道 \(i - 1\) 可以填 \(n - i + 2\),但是 \(i\) 已经占掉了一个前面的,所以变成了 \(n - i + 1\),然后同理我们可以得到 \(i - 1\) 可以放 \(n - i + 2\) 种,但是由于 \(i - 1\) 又占了一个,所以也为 \(n - i + 1\) 以此类推我们可以得到前 \(j\) 的方案数:\((n - i + 1) ^ j\),不过我们在实际情况下会存在一个断点,是的前面的是小于等于 \(j\) 的填数,而其他的在后面,那么其他的为 \(n - (i - 1) + 1\) 种数,那么方案数便是 \((n - i + 2) ^ {i - j - 1}\)
整理一下柿子:
\(ans=n+1+\sum\limits_{i=1}^{n}(\sum\limits_{j=1}^{i-1}((n-i+1)^j(n-i+2)^{i-j-1})(n-i)!i)\)
考虑一下换元,设 \(x = n - i + 1\),设 \(y = \frac{x}{x+1}\) 那么可得:
\(ans=n+1+\sum\limits_{i=1}^{n}(\sum\limits_{j=1}^{i-1}((x)^j(x + 1)^{i-j-1})(n-i)!i)\)
\(ans=n+1+\sum\limits_{i=1}^{n}(\sum\limits_{j=1}^{i-1}((x)^j(x + 1)^{i-1}(x+1)^{-j})(n-i)!i)\)
\(ans=n+1+\sum\limits_{i=1}^{n}((x + 1)^{i-1}\sum\limits_{j=1}^{i-1}((x)^j(x+1)^{-j})(n-i)!i)\)
\(ans=n+1+\sum\limits_{i=1}^{n}((x + 1)^{i-1}\sum\limits_{j=1}^{i-1}(\frac{x}{x+1})^{j}(n-i)!i)\)
\(ans=n+1+\sum\limits_{i=1}^{n}((x + 1)^{i-1}\sum\limits_{j=1}^{i-1}y^{j}(n-i)!i)\)
\(ans=n+1+\sum\limits_{i=1}^{n}((x + 1)^{i-1}\sum\limits_{j=1}^{i-1}\frac{y^2-y}{y-1}(n-i)!i)\)
然后就可以 \(O(n)\) 解决了
1919
考虑将一个区间查询变成前缀异或和,那么设 \(S(x)\) 为 \(1\) 到 \(x\) 的答案,考虑每次消掉最大的fib,我们每次删掉最大的fib,然后分为两段进行计算,然后计算被消掉的数量,如果为奇数则异或后不为 0 所以异或即可。
810
完全背包的优化,如果有连续的 \(min\) 个的可以,那么接下来的肯定都能被表示,所以直接剪枝
总结
数学场我是一点都写不了啊!
智乃的差分
构造。What can I say
牛牛的旅行
一眼淀粉质,但是可以将贡献拆成点带来的和边带来的,那么可以对每条边求出经过次数,对于每个点,从小到大排序,然后将以这个为路径上最大的点对计算贡献,具体来说就是将并查集合并在合并的过程中,计算两个并查集经过最大点的点对数
第K排列
用 dp 优化搜索,我们可以想到一个暴力,就是暴力枚举填什么,然后最后再去判,这个的依据是 k 很小,不过我们发现可能判出很多没必要的,所以我们可以计算一下从后往前的最大填法,如果用最大填法都无法达到目标就没有必要继续填了,所以我们先维护一个从后往前的最大填法 dp,然后暴力从前往后填,好像是叫 A* 吧。
牛牛的 border
考虑枚举子串作为 border,假设这个出现了 cnt 次,长度为 len,那么以这个为 border 的字串将会有 \(\frac{cnt * (cnt - 1)}{2}\) 个(组合数),然后考虑到是 border,那么我们可以从中删掉一些字符,使得还可以作为 border,所以还要计算删掉一些字符,我们考虑用 SAM 这样就确定了左端点,所以我们就只用删掉后缀,那么便可以用等差数列计算了即:\(\frac{len * (len + 1)}{2}\)(因为对于 1 ~ len 的长度都会出现在删掉后缀的 border 中),然后如果要用 SA 就需要处理一些重复的位置,我的评价是,SAM 这个数据结构,甚至说 trie 树都直接将这个多余的一部给剩了(重复的将会合并在条路径中)
#include <iostream>
using namespace std;
using ll = long long;
const int MaxN = 1e5 + 10;
namespace SAM {
ll nxt[MaxN << 1][27], fail[MaxN << 1], len[MaxN << 1], cnt[MaxN << 1], t[MaxN << 1], p[MaxN << 1], tot;
void copy(int x, int y) {
for (int i = 0; i < 27; i++) nxt[x][i] = nxt[y][i];
fail[x] = fail[y];
}
void insert(string s) {
tot = 1;
for (int i = 0, c, to, p, lstp = 1; i < s.size(); i++, nxt[lstp][c] = to, lstp = to) {
c = s[i] - 'a', len[to = ++tot] = len[p = lstp] + 1, cnt[to] = 1;
for (p; p && !nxt[p][c]; p = fail[p]) {
nxt[p][c] = to;
}
if (!p && (fail[to] = 1)) continue;
int v = nxt[p][c], cl = ++tot;
if (len[v] == len[p] + 1 && (tot--, fail[to] = v)) continue;
for (copy(cl, v), len[cl] = len[p] + 1; p && nxt[p][c] == v; p = fail[p]) {
nxt[p][c] = cl;
}
fail[to] = fail[v] = cl;
}
for (int i = 1; i <= tot; i++) t[len[i]]++;
for (int i = 1; i <= tot; i++) t[i] += t[i - 1];
for (int i = 1; i <= tot; i++) p[t[len[i]]--] = i;
for (int i = tot; i >= 1; i--) cnt[fail[p[i]]] += cnt[p[i]];
}
} // namespace SAM
using namespace SAM;
string s;
ll ans;
int n;
int main() {
ios::sync_with_stdio(0), cin.tie(0);
freopen("border.in", "r", stdin);
freopen("border.out", "w", stdout);
cin >> n >> s;
insert(s);
for (int i = 1; i <= tot; i++) {
ans += cnt[p[i]] * (cnt[p[i]] - 1) / 2 * (len[p[i]] + len[fail[p[i]]] + 1) * (len[p[i]] - (len[fail[p[i]]] + 1) + 1) / 2;
}
cout << ans << endl;
return 0;
}
总结
死磕 B 题,然后写了个淀粉质常数巨大后 T 爆,A 题想到了可还是有情况没想全,最后爆 0
你相信()吗
考场上推了三个小时,然后差一步,最后 30 呜呜呜。
考虑推个柿子:
\(a + \frac{b + c}{2} + \frac{d}{4} \ge A\)
\(d + \frac{b + c}{2} + \frac{a}{4} \ge D\)
\(b + \frac{a + d}{2} + \frac{c}{4} \ge B\)
\(c + \frac{a + d}{2} + \frac{b}{4} \ge C\)
我们设 \(a = 4k+d\),然后进行推柿子,于是我们可以通过关于 \(BC\) 的柿子和 \(k\) 的范围来判断可行性,然后就用二分暴力算即可
奇怪的函数
哇,这道题没做出来简直唐诗(其实我没怎么看题)。
一眼线段树,于是考虑信息与标记,我们会发现可以将最终的函数看作一个取值范围的区间,但是考虑到有加减操作,于是我们单纯维护取值范围,并用一个标记维护加减操作
- 信息与信息
如果信息维护的区间有相交,那么合并后的区间将会是相交部分吗,如果没有的话,那么肯定是左边的所有可能值到右边后全变小或全变大,所以区间将会是变小后的值或是变大后的值
- 信息与标记
考虑到信息维护的区间是不加加减操作的取值范围,所以在采用加减操作时只要把左右两端都加上加减操作即可。
- 标记与标记
直接相加,因为都是加减操作
#include <iostream>
using namespace std;
const int MaxN = 3e5 + 10, inf = 1e9;
struct S {
int l, r, w;
S operator+(const S &j) const {
if (r < j.l - w) return {j.l - w, j.l - w, w + j.w};
if (l > j.r - w) return {j.r - w, j.r - w, w + j.w};
return {max(j.l - w, l), min(j.r - w, r), w + j.w};
}
} d[MaxN << 2];
int n, m, op, p, x;
void update(int k, int op, int x, int l = 1, int r = n, int p = 1) {
if (l == r) {
if (op == 1) d[p] = {-inf, inf, x};
if (op == 2) d[p] = {-inf, x, 0};
if (op == 3) d[p] = {x, inf, 0};
return;
}
int mid = l + r >> 1;
if (k <= mid) update(k, op, x, l, mid, p << 1);
if (k > mid) update(k, op, x, mid + 1, r, p << 1 | 1);
d[p] = d[p << 1] + d[p << 1 | 1];
}
int main() {
freopen("function.in", "r", stdin);
freopen("function.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> op >> x, update(i, op, x);
}
for (cin >> m; m; m--) {
cin >> op;
if (op == 4) {
cin >> x;
cout << (x < d[1].l ? d[1].l + d[1].w : x > d[1].r ? d[1].r + d[1].w : x + d[1].w) << endl;
} else {
cin >> p >> x;
update(p, op, x);
}
}
return 0;
}