来自学长的馈赠4 社论

A. 活动投票
主元素问题,一个经典做法是摩尔投票法(Boyer–Moore majority vote algorithm).
大概就是维护一个计数器 \(c\) 和目前答案 \(A\) .
每次考虑加入一个数 \(x\):
- 如果 \(c=0\),则直接 \(A=x\),\(c=1\) .
- 若不然,如果 \(A\neq x\),则 \(c\gets c-1\),否则 \(c\gets c+1\) .
正确性显然 只可意会,不可言传
其他做法不想说了 .
Code
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
int ans, x, n;
int main()
{
long long x = ans;
scanf("%d", &n);
int cc = 0, ans = -1;
for (int i=1; i<=n; i++)
{
scanf("%d", &x);
if (ans == -1){++cc; ans = x;}
else if (x != ans)
{
if (cc) --cc;
else{++cc; ans = x;}
}
else ++cc;
}
printf("%d\n", ans);
return 0;
}
关于摩尔:摩尔,即 mol,\(1\text{ mol}\) 是精确包含 \(6.02214076\times10^{23}\) 个原子或分子等基本单元的系统的物质的量 .
B. 大佬
首先根据期望线性性,答案就是 \([1,k]\) 的答案乘 \(n-k+1\) .
于是问题就变成一个序列 \(\{a\}\),每个元素在 \([1,m]\) 均匀随机,问最大值期望 .
这个可以简单容斥,详见 SoyTony .
这个太 simple 了,我们如何让它 exciting 一点呢?
考虑维护序列 \(\{p\}\),\(p_k\) 表示最大值为 \(k\) 的概率,于是答案就是 \(\displaystyle\sum_{k\ge 0}a_kp_k\) .
定义魔怔运算为:
令 \(\{s\}\) 序列是全为 \(\dfrac 1m\) 的序列 .
考虑在原序列末尾追加一个数产生的贡献,根据简单容斥我们可以发现 \(p\gets p\circ s\)(怎么还要简单容斥
显而易见 \(\circ\) 具有结合律,直接快速幂即可 .
感谢 zcyzcy 提出这个 exciting 的算法 .
魔怔运算可以前缀和做到线性。
时间复杂度 \(O(m\log k)\),简单容斥的复杂度也是一样的 .
Code
using namespace std;
const int P = 1e9+7;
typedef vector<int> vi;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
int n, m, k;
inline int qpow(int a, int n)
{
int ans = 1;
while (n)
{
if (n & 1) ans = 1ll * ans * a % P;
a = 1ll * a * a % P; n >>= 1;
} return ans;
}
inline int inv(int x){return qpow(x, P-2);}
inline vi conv(vi a, vi b)
{
int n = a.size(); assert(a.size() == b.size());
for (int i=1; i<n; i++) (a[i] += a[i-1]) %= P;
for (int i=1; i<n; i++) (b[i] += b[i-1]) %= P;
vi ans(n);
ans[0] = 1ll * a[0] * b[0] % P;
for (int i=1; i<n; i++) ans[i] = ((1ll * a[i] * (b[i] - b[i-1]) % P + 1ll * b[i] * (a[i] - a[i-1]) % P - 1ll * (a[i] - a[i-1]) * (b[i] - b[i-1]) % P) % P + P) % P;
return ans;
}
inline vi qpow(vi a, int n)
{
vi ans(a.size()); ans[0] = 1;
while (n)
{
if (n & 1) ans = conv(ans, a);
a = conv(a, a); n >>= 1;
} return ans;
}
int main()
{
scanf("%d%d%d", &n, &m, &k); int ii = inv(m);
vi a; a.resize(m); vi b(m, ii);
for(int i=0; i<m; i++) scanf("%d", &a[i]);
b = qpow(b, k);
int ans = 0;
for (int i=0; i<m; i++) (ans += 1ll * a[i] * b[i] % P) %= P;
printf("%lld\n", 1ll * ans * (n-k+1) % P);
return 0;
}
但是数据范围那么小为什么要 \(O(m\log k)\) 啊 .
听说有 DP 做法 /yun
C. Dp搬运工3
往空位里插排列 . 令 \(dp_{i,j,k}\) 表示做到 \([1,i]\),有 \(j\) 个配对,\(\operatorname{magic}(A,B)=k\) 的答案 .
于是讨论一下就可以转移了,\(O(n^3)\) .
有一个做法是钦定 \(B\) 为标准排列然后算 \(A\),没咋看 .
Code
using namespace std;
const int N = 111, P = 998244353;
typedef vector<int> vi;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
int n, kkk, dp[N][N][N*N];
int main()
{
scanf("%d%d", &n, &kkk); dp[0][0][0] = 1;
for (int i=0; i<n; i++)
for (int j=max(0, 2*i-n); j<=i; j++)
for (int k=0; k<=n*n; k++)
{
int p = (i - j) << 1, q = n - j - p;
(dp[i+1][j][k] += 1ll * dp[i][j][k] * q % P * (q-1) % P) %= P;
(dp[i+1][j+1][k+i+1] += 1ll * dp[i][j][k] * q % P) %= P;
(dp[i+1][j+1][k+i+1] += 1ll * dp[i][j][k] * p % P * q % P) %= P;
(dp[i+1][j+2][k+2*(i+1)] += 1ll * dp[i][j][k] * (p>>1) % P * (p>>1) % P) %= P;
}
int ans = 0;
for (int i=kkk; i<=n*n; i++) (ans += dp[n][n][i]) %= P;
printf("%d\n", ans);
return 0;
}
D. Beautiful
题目链接 .
NOIP 模拟赛出 *2900 正常吗?
试图魔改 Cantor 展开计算带限制排列字典序,失败
下面复读一波题解 .
约定:错排数为 \(D_n=(n-1)(D_{n-1}+D_{n-2})\) .
广义错排数 \(D_{n,m}\) 表示 \(1\sim n\) 的排列有 \(m\) 个限制(形如第 \(i\) 位不能为 \(i\))的方案数 .
边界:
- \(m=0\):\(D_{n,m}=n!\) .
- \(n=m\):\(D_{n,m}=D_n\) .
于是只剩下 \(n<m\) 情况,钦定 \(i\) 不被限制,于是考虑第 \(i\) 位放的是被限制的还是不被限制的即可,递推式:
这个可以直接 \(O(n^2)\) 干 .
考虑直接扫,然后后面的贡献就是错排数的次幂,前面已经算过了,只需要算当前行的贡献(类似 Cantor 展开)
设当前扫到 \((i,j)\),于是对于当前行剩下 \(n−j\) 个元素,它们的填法受到 \(b_{i−1,j}\sim b_{i−1,n}\)的限制,但不完全受到限制。具体地,求出有多少个数 \(v\) 使得 \(v\) 没有 在 \(b_{i,1}\sim b_{i,j}\) 当中出现过,且没有在 \(b_{i-1,1}\sim b_{i-1,j}\) 当中出现过,记作 \(L\),那么相当于是 \(n−j\) 阶排列有多少个限制了 \(L\) 个位置的错排,就是广义错排数 \(D_{n-j,L}\) .
为了方便记 \(\operatorname B_i(l,r)\) 为 \(b_{i,l}\sim b_{i,r}\) 组成的集合 .
现在考虑如何快速求 \(L\) . 因为唯一不确定因素是 \(b_{i,j}\),所以我们先求出满足 \(v\in\operatorname B_i(1, j-1)\land v\notin\operatorname B(1, j)\) 的 \(v\) 的个数 \(L\),\(b_{i,j}\) 对真正 \(L\) 的贡献最多只是当 \(b_{i,j}\notin\operatorname B_{i-1}(1,j)\) 时要 \(L\gets L-1\) .
因此,只需在 \(v\notin\operatorname B_i(1,j-1)\) 的前提下,维护存在多少 \(v\) 使得 \(v\notin\operatorname B_{i-1}(1,j)\),并且支持查询 \(\le V\) 的 \(v\) 的个数 .
对当前行和上一行分别开一个权值树状数组就可随手维护,注意 \(b_{i,j}\notin\operatorname B_{i-1}(1,j)\) 时要减一下,后面还得加回去 .
时间复杂度 \(O(n^2\log n)\),注意原题是 0-indexed,模拟赛模数变一下,并且 1-indexed,不要直接 cv 了 .
Code (CF1085G)
using namespace std;
const int N = 2222, P = 998244353;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
int n, k, fac[N], a[N][N], d[N][N], pwd[N];
struct FenwickTree
{
int a[N];
inline void clear(){memset(a, 0, sizeof a);}
inline int lowbit(int x) {return x & -x;}
inline void add(int i, int x){if (!i) return ; for (; i<=n; i+=lowbit(i)) (a[i] += x) %= P;}
inline int query(int i){int ans = 0; for (; i; i&=i-1) (ans += a[i]) %= P; return ans;}
}null, full, T, nT;
void init()
{
d[0][0] = pwd[0] = 1;
for (int i=1; i<=n; i++)
for (int j=0; j<=i; j++)
{
if (!j) d[i][j] = 1ll * d[i-1][0] * i % P;
else if (i==j) d[i][i] = ((i == 1)? 0 : 1ll * (i-1) * (d[i-1][i-1] + d[i-2][i-2]) % P);
else d[i][j] = (1ll * j * d[i-1][j-1] % P + 1ll * (i-j) * d[i-1][j] % P) % P;
}
for (int i=1; i<=n; i++) pwd[i] = 1ll * pwd[i-1] * d[n][n] % P;
for (int i=1; i<=n; i++) full.add(i, 1);
}
bool A[N], B[N];
int main()
{
scanf("%d", &n); init();
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++) scanf("%d", a[i]+j);
int ans = 0;
for (int i=1; i<=n; i++)
{
memset(A, false, sizeof A); memset(B, false, sizeof B);
int L = n; T = null; nT = full;
ll _ = 0;
for (int j=1; j<=n; j++)
{
if (!B[a[i-1][j]]){--L; nT.add(a[i-1][j], -1);}
(_ += 1ll * T.query(a[i][j] - 1) * d[n-j][(i>1) * L] % P) %= P;
if (L || (i == 1)) (_ += 1ll * nT.query(a[i][j] - 1) * d[n-j][(i>1) * (L-1)] % P) %= P;
if (A[a[i][j]]) T.add(a[i][j], -1);
else nT.add(a[i][j], -1);
A[a[i-1][j]] = B[a[i][j]] = 1;
if (!B[a[i-1][j]]) T.add(a[i-1][j], 1);
if (!A[a[i][j]]) --L;
}
(ans += 1ll * _ * pwd[n-i] % P) %= P;
} printf("%d\n", ans);
return 0;
}
以下是博客签名,正文无关
本文来自博客园,作者:yspm,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/16515493.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ

浙公网安备 33010602011771号