A.gift
考虑交换次数是置换的阶 (-1),那么答案就是 (n-) 环数
考虑把已知的连通块表示一下,链首和链尾可能分别是 (0 o id,id o 0,0 o 0,id o id)
已经是环的部分直接统计答案
求出来这几种情况的方案是 (trivial) 的,这个直接做就行
这四种环需要合并,可以用 (0 o 0) 来作为 (id o 0,0 o id) 的媒介,(id o id) 的部分会在 $0 o 0 $ 中出现
设 (f_i) 为 (i) 个环的方案数,(g_i) 是至少 (i) 个环的
钦定 (j) 个形成 (i) 个环,剩下的随便连,得到式子
(g_i=sum_{j=i}^n inom n jegin{bmatrix}i\ jend {bmatrix} A(n+m-j,n-j))
二项式反演得到
[f(n)=sum_{i=n}^m inom i n g(i)Leftrightarrow g(n)=sum_{i=n}^m (-1) ^{i-n} inom i n f(i)
]
[f_i=sum_{j=i}^ninom nj egin{bmatrix} i\ j end{bmatrix}frac{(n+m-j-1)!}{(m-1)!}
]
那么分别对 $c_{0,id},c_{id,0} $ 作为多项式的次数进行卷积即可
B.girls
先容斥,答案为不考虑互斥减掉至少一个互斥的,再减掉两个互斥的,加上图上三元环的带权贡献
貌似分开计数就行了,图上三元环计数的方法是按照出度排序,原图中的边,小的点向大的点连边
枚举 (1 o n),先标记出点,再在出点的出点里面找被标记点就行了
#include <bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define int long long
#define For(i,a,b) for(register int i=a;i<=b;++i)
#define Down(i,a,b) for(register int i=a;i>=b;i--)
#define reg register
inline int min(int x, int y) {
return x < y ? x : y;
}
inline int max(int x, int y) {
return x > y ? x : y;
}
inline void swap(int &x, int &y) {
int t = x;
x = y;
y = t;
return ;
}
namespace yspm {
inline int read() {
int res = 0, f = 1;
char k;
while (!isdigit(k = getchar()))
if (k == '-')
f = -1;
while (isdigit(k))
res = res * 10 + k - '0', k = getchar();
return res * f;
}
const int N = 2e5 + 10;
int head[N], cnt, n, m, id[N], q[N], deg[N];
vector<int>f[N], g[N];
ull A, B, C, ans;
bool vis[N];
struct node {
int to, nxt;
} e[N << 1];
inline bool cmp(int x, int y) {
return deg[x] > deg[y];
}
inline void add(int u, int v) {
e[++cnt].nxt = head[u];
e[head[u] = cnt].to = v;
deg[u]++;
f[u].push_back(v);
return ;
}
signed main() {
// freopen("1.in","r",stdin);
n = read();
m = read();
A = read(), B = read(), C = read();
for (reg int i = 1, u, v; i <= m; ++i)
u = read(), v = read(), add(u, v), add(v, u);
for (reg int i = 0; i < n; ++i) {
sort(f[i].begin(), f[i].end());
if (i + 2 < n)
ans += A * ((n - i - 2) * (n - i - 1) / 2) * i;
if (i + 1 < n)
ans += (i * (n - i - 1)) * B * i;
if (i - 2 >= 0)
ans += (i - 1) * i / 2 * C * i;
}
for (reg int x = 0; x < n; ++x)
for (reg int i = head[x]; i; i = e[i].nxt) {
int t = e[i].to;
if (t < x)
continue;
ans -= (n - 1 - t) * (x * A + t * B) + C * ((n - 1) * n / 2 - (t + 1) * t / 2);
ans -= (t - x - 1) * (x * A + t * C) + B * (t * (t - 1) / 2 - (x + 1) * x / 2);
ans -= x * (x * B + t * C) + A * x * (x - 1) / 2;
}
for (reg int x = 0; x < n; ++x) {
ull sum = 0, c = 0;
for (reg int i = head[x]; i; i = e[i].nxt)
if (e[i].to > x)
sum += e[i].to, ++c;
for (reg int i = head[x]; i; i = e[i].nxt)
if (e[i].to < x)
ans += e[i].to * A * c + x * B * c + sum * C;
c = 0;
sum = 0;
for (reg int j = ((int)f[x].size()) - 1; j >= 0 && f[x][j] > x; --j)
ans += x * A * c + f[x][j] * B * c + sum * C, sum += f[x][j], ++c;
sum = 0;
c = 0;
for (reg int i = 0; i < f[x].size() && f[x][i] < x; ++i)
ans += sum * A + f[x][i] * B * c + x * C * c, sum += f[x][i], ++c;
}
for (reg int i = 0; i < n; ++i)
q[i] = i;
sort(q, q + n, cmp);
for (reg int i = 0; i < n; ++i)
id[q[i]] = i;
For(x, 0, n - 1) for (reg int i = head[x]; i; i = e[i].nxt)
if (id[e[i].to] > id[x])
g[x].push_back(e[i].to);
For(i, 0, n - 1) {
int now = q[i];
for (auto p : g[now])
vis[p] = 1;
for (auto t : g[now])
for (auto z : g[t]) {
if (!vis[z])
continue;
int fir = now, sec = t, thi = z;
if (sec < fir)
swap(sec, fir);
if (thi < fir)
swap(thi, fir);
if (thi < sec)
swap(sec, thi);
ans -= sec * B + fir * A + thi * C;
}
for (auto p : g[now])
vis[p] = 0;
}
printf("%lld
", ans);
return 0;
}
}
signed main() {
return yspm::main();
}
C.string
题目中的要求都是广义 (sam) 的基操
写个支持链加的 (lct) 每个点上维护 (20) 个串的 (endpos) 信息
对于版本,开 (n) 个 (vector) 记一下每个串最后一次被修改,查的时候 (upper\_bound-1)
剩下的就是写了