文以载道(潍坊一中78级互测赛)ABEF题解
__vector__ · · 题解
A
显然有一个结论:假设
那么能不能推广一下呢?
事实上,只要不存在
以下是我的(并不严谨的)证明:
假设
1 到n 位置顺序是从左到右。考虑反复执行这个操作:
选择最靠左的不为0 的位置,记该位置的值为a_{lef} ,从该位置开始向右选择最长的最小值大于等于a_{lef} 的连续子段,对这个子段执行a_{lef} 次操作。
由于a_i \le a_{i-1} + a_{i+1} ,显然可得,除非序列变为全0 ,否则不会出现不可操作的情况。
设计
假设第
最后答案是
用前缀和优化后复杂度是
CF 提交记录
B
此时
现在,我们应该请出数数题的一个很好用的算法:容斥!
如果一个位置
设
那么根据容斥公式,答案是
我们需要对于
设计状态
看上去状态数是
转移的话,假设当前状态是
- 钦定第
i-1 个位置是坏位置dp_{i,lst,x} = \sum\limits_{lst_2}dp_{i-2,lst_2,x \oplus 1} \times (k-lst-lst_2) - 不钦定第
i-1 个位置是坏位置(注意语序,不要理解成 “钦定第i-1 个位置不是坏位置”)dp_{i,lst,x} = \sum\limits_{lst_2} dp_{i-1,lst_2,x}
同理,前缀和优化可以做到
CF 提交记录
E
这些操作看上去很麻烦,而且似乎没有特定数据结构可以直接完成如此复杂的操作。
那么我们请出递推重器——矩阵!
对于一个元素有多个属性需要互相递推的操作,使用矩阵将会大大简化,所以考虑使用矩阵完成这些操作。
剩下部分
F
考虑枚举中点,计算每一个点作为回文串中点能给答案带来多少贡献。
具体地,
假设枚举
复杂度
Code(By @CountingGroup)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
inline int read () {
int x = 0, f = 0;
char c = getchar ();
for ( ; c < '0' || c > '9' ; c = getchar ()) f |= (c == '-');
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
return !f ? x : -x;
}
inline int ksm (int a, int b, int p) {
int res = 1;
while (b) {
if (b & 1) res = res * a % p;
b >>= 1;
a = a * a % p;
}
return res;
}
const int N = 4005;
const int mod = 998244353;
int n, mp[N], ans, tot, pre[N], suf[N], siz[N];
signed main () {
n = read ();
tot = 1;
pre[0] = suf[n + 1] = 1;
for (int i = 1;i <= n; ++ i) {
char s[40];
scanf ("%s", s + 1);
int m = strlen (s + 1);
for (int j = 1;j <= m; ++ j) {
int offset = s[j] - 'a';
mp[i] |= (1 << offset);
}
tot = tot * m % mod;
siz[i] = m;
}
for (int i = 1;i <= n; ++ i) pre[i] = pre[i - 1] * siz[i] % mod;
for (int i = n;i >= 1; -- i) suf[i] = suf[i + 1] * siz[i] % mod;
for (int mid = 1;mid <= n; ++ mid) {
int L = mid, R = mid, cur = __builtin_popcountll (mp[mid]);
while (true) {
ans = (ans + cur * pre[L - 1] % mod * suf[R + 1] % mod) % mod;
L --, R ++;
if (L < 1 || R > n) break;
int state = (mp[L] & mp[R]);
cur = cur * (__builtin_popcountll (state)) % mod;
}
}
for (int mid = 1;mid <= n; ++ mid) {
int S = (mp[mid] & mp[mid + 1]);
int L = mid, R = mid + 1, cur = __builtin_popcount (S);
while (true) {
ans = (ans + cur * pre[L - 1] % mod * suf[R + 1] % mod) % mod;
L --, R ++;
if (L < 1 || R > n) break;
int state = (mp[L] & mp[R]);
cur = cur * (__builtin_popcountll (state)) % mod;
}
}
printf ("%lld\n", ans * ksm (tot, mod - 2, mod) % mod);
return 0;
}