相隔大约一周才补题,原因是太弱了。
H 正解是分治 FFT,目前不会所以留坑在这里。
\(\mathcal A\sim E\)
赛时平均一题 5 min,而且主要是读题时间,所以放在一起提一句。
A 异或基本运算,B 排序,C 离散化,D DFS 序,E 0/1 BFS,每以一个障碍物为中心的 \(3\times 3\) 均为 \(1\) 步到达。
代码懒得放。
\(\mathcal F\)
针对一个字符串的每个后缀,求出它与所有后缀的 LCP 之和。
比较套路的 SA + 单调栈。
考虑 SA,每个点对当前位的贡献通过单调栈简单维护即可,正反各扫一遍。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
int n, tot, stk[N], wid[N];
int bac[N], sa[N], rk[N], SA[N], RK[N], lcp[N];
char str[N]; LL ans[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 Get_SA(){
for(int i = 1; i <= n; i ++) bac[str[i]] ++;
for(int i = 1; i <= 256; i ++) bac[i] += bac[i - 1];
for(int i = 1; i <= n; i ++) sa[bac[str[i]] --] = i;
for(int i = 1; i <= n; i ++) rk[sa[i]] = rk[sa[i - 1]] + (str[sa[i]] != str[sa[i - 1]]);
for(int p = 1; p <= n; p <<= 1){
for(int i = 1; i <= n; i ++) bac[rk[sa[i]]] = i;
for(int i = n; i >= 1; i --) if(sa[i] > p) SA[bac[rk[sa[i] - p]] --] = sa[i] - p;
for(int i = n; i > n - p; i --) SA[bac[rk[i]] --] = i;
#define comp(x, y) (rk[x] != rk[y] || rk[x + p] != rk[y + p])
for(int i = 1; i <= n; i ++) RK[SA[i]] = RK[SA[i - 1]] + comp(SA[i - 1], SA[i]);
for(int i = 1; i <= n; i ++) sa[i] = SA[i], rk[i] = RK[i];
if(rk[sa[n]] == n) break;
}
for(int i = 1; i <= n; i ++){
int j = sa[rk[i] - 1], k = max(lcp[rk[i - 1]] - 1, 0);
while(str[i + k] == str[j + k] && str[i + k]) k ++;
lcp[rk[i]] = k;
}
}
int main(){
scanf("%d", &n);
scanf("%s", str + 1);
Get_SA();
LL now;
tot = now = 0;
for(int i = 2; i <= n; i ++){
int width = 1;
while(tot && stk[tot] >= lcp[i]){
now -= 1LL * wid[tot] * stk[tot];
width += wid[tot];
tot --;
}
stk[++ tot] = lcp[i]; wid[tot] = width;
now += 1LL * wid[tot] * stk[tot];
ans[sa[i]] += now;
}
tot = now = 0;
for(int i = n; i >= 1; i --){
ans[sa[i]] += now;
int width = 1;
while(tot && stk[tot] >= lcp[i]){
now -= 1LL * wid[tot] * stk[tot];
width += wid[tot];
tot --;
}
stk[++ tot] = lcp[i]; wid[tot] = width;
now += 1LL * wid[tot] * stk[tot];
}
for(int i = 1; i <= n; i ++) printf("%lld\n", ans[i] + n - i + 1);
return 0;
}
\(\mathcal G\)
给定一个无向图,任意选择一些边,对于每个点 \(2\leq k\leq n\),求出使 \(1,k\) 联通的方案数。
这个大写 G 的标题怎么这么鬼畜