20240329
T1
NFLSOJ P60000 镜中故我
通过找规律,能够发现序列一定时答案随 \(k\) 的变化是杨辉三角的某一行,再乘个 \(1\) 或 \(-1\) 的系数。所以只需要知道到底是哪一行。算出 \(k = 1\) 时的答案即可。能够发现 \(k= 1\) 时的答案就是原序列后缀最小值的个数。所以直接组合数计算即可。
证明:考虑一对逆序 \(x, y\),对于每一个选了 \(x\) 而不选 \(y\) 的选择,我们都可以通过选上 \(y\) 来给序列的长度 \(+1\) 而保持序列合法。所以删去 \(x\) 对答案无影响。所以只需要保留所有原序列的后缀最小值,然后在这序列中任选出 \(k\) 个数。所以就是组合数。
代码
#include <iostream>
using namespace std;
const int P = 998244353;
long long fac[10000005], ifac[10000005], inv[10000005];
int a[10000005];
void Cpre(int n) {
fac[0] = ifac[0] = inv[0] = fac[1] = ifac[1] = inv[1] = 1;
for (int i = 2; i <= n; i++) {
fac[i] = fac[i - 1] * i % P;
inv[i] = (P - P / i) * inv[P % i] % P;
ifac[i] = ifac[i - 1] * inv[i] % P;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
Cpre(n);
int b = 0;
int smn = 2147483647;
for (int i = n; i; i--) {
smn = min(smn, a[i]);
b += (a[i] <= smn);
}
cout << (fac[b] * ifac[k] % P * ifac[b - k] % P * ((k & 1) ? -1 : 1) + P) % P << "\n";
return 0;
}
T2
NFLSOJ P60001 K4
这个故事告诉我们,\(\frac{n^2}{\omega}\) 能艹过 \(1e5\)。
结论:将无向图的所有边从度数小的往度数大的连,则每个点的出度不会超过根号级别。
感性理解一下,每个点连向的点度数都大于等于自身度数,如果出边超过了根号,则总度数就要爆炸了。所以不会超。
所以只需要枚举第一个点 \(x\),然后枚举其连向的所有点 \(y\) 并打标记,再枚举 \(y\) 连向的所有点 \(z\),如果 \(z\) 有标记,则在 bitset 中记录 \(y \rightarrow z\)。然后再枚举 \(y\),枚举 \(z\),如果 \(z\) 有标记,就把 \(y\) 和 \(z\) 的 bitset 与起来,数一下 \(1\) 的个数,计入答案即可。
代码
#include <iostream>
#include <bitset>
#include <vector>
using namespace std;
vector<int> vec[100005];
int head[100005], nxt[200005], to[200005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
bitset<505> f[505];
int n, m;
int lim;
int id[100005], icnt;
int u[100005], v[100005];
int deg[100005];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> u[i] >> v[i];
deg[u[i]]++, deg[v[i]]++;
}
for (int i = 1; i <= m; i++) {
if (deg[u[i]] > deg[v[i]])
swap(u[i], v[i]);
vec[u[i]].emplace_back(v[i]);
}
int ans = 0;
for (int i = 1; i <= n; i++) {
icnt = 0;
for (int v : vec[i]) id[v] = ++icnt, f[icnt].reset();
for (int j : vec[i])
for (int k : vec[j])
if (id[k])
f[id[j]][id[k]] = 1;
for (int j : vec[i])
for (int k : vec[j])
if (id[k])
ans += (f[id[j]] & f[id[k]]).count();
for (int v : vec[i]) id[v] = 0;
}
cout << "2021 06 52 " << ans << "\n";
return 0;
}
T3
NFLSOJ P60002 斜二进制
首先有 \(dp[i][j]\) 表示前 \(i\) 个数,是否能选出和若干数为 \(j\)。使用 bitset 优化可以做到 \(O(\frac{nS}{\omega})\)。发现接下来做不了一点了,但是发现 \(1\) 的个数很多,所以设一个对 \(1\) 更友好的状态:\(dp[i - j][j]\) 表示和减去个数为 \(i - j\),选了 \(j\) 个数的可行性。可以发现 \(i - j\) 固定时第二维里会形成若干区间,而这区间个数最多是 4 个。因为每个区间的长度至少为 \(cnt_1 + 1\),如果有五个,总长度就是 \(5cnt_1 + 5\),而 \(cnt_1\) 的个数限制保证了这个总长度会爆炸,所以至多就是 \(4\) 个区间。转移跟 bitset 做法一样,只是把或换成合并区间。
代码
#include <iostream>
#include <vector>
#include <algorithm>
#define int long long
using namespace std;
int cnt[200005];
vector<pair<int, int> > vec[400005];
int L, R;
void Merge(vector<pair<int, int> >& v1, vector<pair<int, int> > v2, int dy) {
vector<pair<int, int> > tmp;
for (auto v : v1) tmp.emplace_back(v.first, v.second);
for (auto v : v2) tmp.emplace_back(v.first + dy, v.second + dy);
sort(tmp.begin(), tmp.end());
int tl, c = 0;
v1.clear();
for (auto v : tmp) {
if (v1.empty() || v1.back().second < v.first)
v1.emplace_back(v);
else if (v1.back().second < v.second)
v1.back().second = v.second;
}
}
void work(int dx, int dy) {
if (dx > 0) {
for (int i = R; i >= L; i--) Merge(vec[i + dx], vec[i], dy);
R += dx;
} else {
for (int i = L; i <= R; i++) Merge(vec[i + dx], vec[i], dy);
L += dx;
}
}
signed main() {
freopen("skew.in", "r", stdin);
freopen("skew.out", "w", stdout);
int n, S;
cin >> n >> S;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
cnt[x]++;
}
vec[L = R = 200000].emplace_back(make_pair(0, cnt[1]));
for (int i = 0; i <= S; i++) {
if (i == 1 || !cnt[i])
continue;
for (int j = 1; j <= cnt[i]; j <<= 1) {
work(j * i - j, j * i), cnt[i] -= j;
}
if (cnt[i])
work(cnt[i] * i - cnt[i], cnt[i] * i);
}
int ans = 0;
for (int i = L; i <= R; i++) {
for (auto v : vec[i])
ans += v.second - v.first + 1;
}
cout << ans << "\n";
return 0;
}
</details>