读错题意导致下大分,????
G 的转化过于神秘,锅了。
\(A\sim D\)A、B、C 模拟,D 差分。
\(E\)求首 $\leq $ 尾的子序列个数。
线段树维护全局 \(\times 2\) 和但单点 \(+1\) 即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 3e5 + 10;
const LL P = 998244353;
int n, a[N], p[N], t, c[N];
LL dat[N << 2], mul[N << 2];
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
void Build(int p, int l, int r) {
dat[p] = 0, mul[p] = 1;
if(l == r) return;
int mid = (l + r) >> 1;
Build(p << 1, l, mid);
Build(p << 1 | 1, mid + 1, r);
}
void Push_Up(int p) {
dat[p] = (dat[p << 1] + dat[p << 1 | 1]) % P;
}
void Push_Down(int p) {
if(mul[p] == 1) return;
int l = p << 1, r = p << 1 | 1;
dat[l] = dat[l] * mul[p] % P, mul[l] = mul[l] * mul[p] % P;
dat[r] = dat[r] * mul[p] % P, mul[r] = mul[r] * mul[p] % P;
mul[p] = 1;
}
void Add(int p, int l, int r, int k) {
if(l == r) {dat[p] = (dat[p] + 1) % P; return;}
Push_Down(p);
int mid = (l + r) >> 1;
if(k <= mid) Add(p << 1, l, mid, k);
else Add(p << 1 | 1, mid + 1, r, k);
Push_Up(p);
}
LL Ask(int p, int l, int r, int L, int R) {
if(L <= l && r <= R) return dat[p];
Push_Down(p);
int mid = (l + r) >> 1; LL sum = 0;
if(L <= mid) sum = (sum + Ask(p << 1, l, mid, L, R)) % P;
if(R > mid) sum = (sum + Ask(p << 1 | 1, mid + 1, r, L, R)) % P;
return sum;
}
int main() {
n = read();
for(int i = 1; i <= n; i ++) a[i] = p[i] = read();
sort(p + 1, p + n + 1);
t = unique(p + 1, p + n + 1) - (p + 1);
Build(1, 1, t);
LL ans = 0;
for(int i = 1; i <= n; i ++) {
int x = lower_bound(p + 1, p + t + 1, a[i]) - p;
ans = (ans + Ask(1, 1, t, 1, x)) % P;
dat[1] = dat[1] * 2 % P;
mul[1] = mul[1] * 2 % P;
Add(1, 1, t, x);
}
printf("%lld\n", ans);
return 0;
}
\(F\)
求树上好的点集个数,一个点集是好的,当且仅当任意两点间的距离均为直径。
根据直径长度奇偶性讨论,所有直径都经过某个点/某条边。
若有中点,那么处理出每个子树中在某个直径上的点数,简单统计即可。
若有中边,直接统计边上两个点,然后相乘即可。
不难证明不会在某个子树中存在统计不到的直径。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
const LL P = 998244353;
int n, dep[N], num[N], frm[N];
bool vis[N];
vector<int> G[N];
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
void dfs1(int u, int fa) {
dep[u] = dep[fa] + 1;
frm[u] = fa;
for(int v : G[u]) if(v != fa) dfs1(v, u);
}
void dfs2(int u, int fa, int lim) {
vis[u] = true;
dep[u] = dep[fa] + 1;
if(dep[u] == lim) num[u] ++;
for(int v : G[u]) if(!vis[v]) dfs2(v, u, lim), num[u] += num[v];
}
int main() {
n = read();
for(int i = 1; i < n; i ++) {
int u = read(), v = read();
G[u].push_back(v);
G[v].push_back(u);
}
dfs1(1, 0);
int s = 0; for(int i = 1; i <= n; i ++) s = dep[s] < dep[i] ? i : s;
for(int i = 1; i <= n; i ++) dep[i] = 0;
dfs1(s, 0);
int t = 0; for(int i = 1; i <= n; i ++) t = dep[t] < dep[i] ? i : t;
if((dep[t] - 1) & 1) {
int u, v;
for(int i = t; i; i = frm[i])
if(dep[i] == dep[t] / 2 + 1) u = i, v = frm[i];
vis[u] = vis[v] = true;
int rec = dep[t];
for(int i = 1; i <= n; i ++) dep[i] = 0;
dfs2(u, 0, (rec - 2) / 2 + 1);
dfs2(v, 0, (rec - 2) / 2 + 1);
printf("%lld\n", 1LL * num[u] * num[v] % P);
}
else {
int u;
for(int i = t; i; i = frm[i])
if(dep[i] == dep[t] / 2 + 1) u = i;
vis[u] = true;
int rec = dep[t];
for(int i = 1; i <= n; i ++) dep[i] = 0;
dfs2(u, 0, (rec - 1) / 2 + 1);
LL sum = 0, ans = 0;
for(int v : G[u]) ans = (ans + 1LL * num[v] * (sum + ans) % P) % P, sum += num[v];
printf("%lld\n", ans);
}
return 0;
}
\(H\)
对于 \(k=1\sim n\),统计长度为 \(k\),没有超过 \(m\) 个相同的数的,和为 \(n\) 的无序序列个数。
将序列升序排序,得到差分数组 \(A\),问题转化为求差分数组的方案数,满足:
- \(\sum A_i\times(k-i+1)=n\)
- \(A_1> 0\)
- 没有 \(\geq m\) 个连续的 \(0\)。
将 \(A\) 翻转,得到 \(\sum A_i\times i=n,A_k>0\), 这样就和 \(k\) 无关了,那么就能够一起统计。
设 \(f(i,j)\) 表示 \(i\) 个数,和为 \(j\) 的方案数,有转移方程:
复杂度螺旋升天。
但是仔细观察,\(f(i,j)\) 与 \(f(i,j-i)\) 的方程仅一线之隔,所以可以改为:
前缀和优化一下,就可以做到 \(O(n^2)\) 了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 5010;
const LL P = 998244353;
int n, m; LL sum[N], f[N][N];
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
int main() {
n = read(), m = read();
f[0][0] = sum[0] = 1;
for(int i = 1; i <= n; i ++) {
for(int j = i; j <= n; j ++)
f[i][j] = (sum[j - i] + f[i][j - i]) % P;
for(int j = 0; j <= n; j ++) {
sum[j] = (sum[j] + f[i][j]) % P;
if(i >= m) sum[j] = (sum[j] + P - f[i - m][j]) % P;
}
}
for(int i = 1; i <= n; i ++) printf("%d\n", f[i][n]);
return 0;
}