2023冲刺国赛自测 9.0
T1 字符串
正解做法是将原问题转化为两棵树点集求交,直接上扫描线。
考场上尝试使用 SAM 解决,由于 SAM 的 parent 树是一个压缩后缀字典树,因此考虑将询问插入到这个字典树中,比较显然的方法是直接树剖,通过二分 + 哈希可以
code
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cassert>
using namespace std;
const int max1 = 2e5;
const int inf = 0x3f3f3f3f;
const int base = 29, mod1 = 1e9 + 7, mod2 = 20051107;
int T, n, q;
char s[max1 + 5];
struct Hash
{
int h1, h2;
Hash () {}
Hash ( int __h1, int __h2 )
{ h1 = __h1, h2 = __h2; }
Hash operator + ( const Hash &A ) const
{ return Hash((h1 + A.h1) % mod1, (h2 + A.h2) % mod2); }
Hash operator - ( const Hash &A ) const
{ return Hash((h1 - A.h1 + mod1) % mod1, (h2 - A.h2 + mod2) % mod2); }
Hash operator * ( const Hash &A ) const
{ return Hash(1LL * h1 * A.h1 % mod1, 1LL * h2 * A.h2 % mod2); }
bool operator == ( const Hash &A ) const
{ return h1 == A.h1 && h2 == A.h2; }
};
struct Hash_Table
{
Hash h[max1 + 5], power[max1 + 5];
void Build ()
{
power[0] = Hash(1, 1);
for ( int i = 1; i <= n; i ++ )
power[i] = power[i - 1] * Hash(base, base);
h[n + 1] = Hash(0, 0);
for ( int i = n; i >= 1; i -- )
h[i] = h[i + 1] * Hash(base, base) + Hash(s[i] - 'a' + 1, s[i] - 'a' + 1);
return;
}
Hash Query ( int L, int R )
{
return h[L] - h[R + 1] * power[R - L + 1];
}
Hash Query ( int x, int y, int len )
{
if ( len <= y )
return Query(n - len + 1, n);
return Query(x - (len - y) + 1, x) + Query(n - y + 1, n) * power[len - y];
}
}H;
struct Suffix_Automaton
{
int trans[max1 * 2 + 5][26], link[max1 * 2 + 5], minpos[max1 * 2 + 5], cnt[max1 * 2 + 5], maxlen[max1 * 2 + 5];
int total, last;
vector <int> edge[max1 * 2 + 5];
int siz[max1 * 2 + 5], son[max1 * 2 + 5], top[max1 * 2 + 5], bottom[max1 * 2 + 5], dfn[max1 * 2 + 5], rk[max1 * 2 + 5], dfs_clock;
void Clear ()
{
memset(trans[0], 0, sizeof(trans[0])); link[0] = -1; minpos[0] = cnt[0] = maxlen[0] = 0;
total = last = 0;
return;
}
void Extend ( int x )
{
int now = ++total, p = last;
memset(trans[now], 0, sizeof(trans[now]));
minpos[now] = x; cnt[now] = 1;
maxlen[now] = maxlen[p] + 1;
last = now;
while ( p != -1 && !trans[p][s[x] - 'a'] )
trans[p][s[x] - 'a'] = now, p = link[p];
if ( p == -1 )
link[now] = 0;
else
{
int q = trans[p][s[x] - 'a'];
if ( maxlen[q] == maxlen[p] + 1 )
link[now] = q;
else
{
int clone = ++total;
memcpy(trans[clone], trans[q], sizeof(trans[q]));
link[clone] = link[q];
link[q] = link[now] = clone;
minpos[clone] = inf; cnt[clone] = 0;
maxlen[clone] = maxlen[p] + 1;
while ( p != -1 && trans[p][s[x] - 'a'] == q )
trans[p][s[x] - 'a'] = clone, p = link[p];
}
}
return;
}
void Find_Heavy_Edge ( int now )
{
siz[now] = 1, son[now] = -1;
int max_siz = 0;
for ( auto v : edge[now] )
{
Find_Heavy_Edge(v);
minpos[now] = min(minpos[now], minpos[v]);
cnt[now] += cnt[v];
siz[now] += siz[v];
if ( siz[v] > max_siz )
max_siz = siz[v], son[now] = v;
}
return;
}
void Connect_Heavy_Edge ( int now, int ancestor )
{
top[now] = ancestor; dfn[now] = ++dfs_clock; rk[dfs_clock] = now;
if ( son[now] != -1 )
Connect_Heavy_Edge(son[now], ancestor);
for ( auto v : edge[now] )
{
if ( v == son[now] )
continue;
Connect_Heavy_Edge(v, v);
}
return;
}
void Build ()
{
Clear();
for ( int i = 1; i <= n; i ++ )
Extend(i);
for ( int i = 0; i <= total; i ++ )
edge[i].clear();
for ( int i = 1; i <= total; i ++ )
edge[link[i]].push_back(i);
dfs_clock = 0;
Find_Heavy_Edge(0); Connect_Heavy_Edge(0, 0);
memset(bottom, -1, sizeof(int) * (total + 1));
for ( int i = 0; i <= total; i ++ )
bottom[top[i]] = max(bottom[top[i]], dfn[i]);
return;
}
int Query ( int x, int y )
{
if ( x + y > n )
return 0;
int now = 0;
while ( true )
{
int L = dfn[now] + 1, R = bottom[now], p = now;
while ( L <= R )
{
int mid = (L + R) >> 1;
if ( maxlen[rk[mid]] <= x + y && H.Query(minpos[rk[mid]] - maxlen[rk[mid]] + 1, minpos[rk[mid]]) == H.Query(x, y, maxlen[rk[mid]]) )
p = rk[mid], L = mid + 1;
else
R = mid - 1;
}
if ( maxlen[p] == x + y )
return cnt[p];
char c = maxlen[p] < y ? s[n - maxlen[p]] : s[x - (maxlen[p] - y)];
int q = -1;
for ( auto v : edge[p] )
if ( s[minpos[v] - maxlen[p]] == c )
{ q = v; break; }
if ( q == -1 )
return 0;
else
{
if ( maxlen[q] >= x + y )
{
if ( H.Query(minpos[q] - x - y + 1, minpos[q]) == H.Query(x, y, x + y) )
return cnt[q];
else
return 0;
}
now = q;
}
}
assert(0);
}
}SAM;
void Work ()
{
scanf("%d%d%s", &n, &q, s + 1); H.Build(); SAM.Build();
int x, y;
while ( q -- )
{
scanf("%d%d", &x, &y);
printf("%d\n", SAM.Query(x, y));
}
}
int main ()
{
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
scanf("%d", &T);
while ( T -- )
Work();
return 0;
}
T2 计树
考虑枚举一个值
不难发现对于每种合法序列
考虑 dp 求解,设
类似树形背包进行转移。
code
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int max1 = 200;
const int mod = 1e9 + 7;
int n, root;
vector <int> edge[max1 + 5];
int C[max1 + 5][max1 + 5];
int size[max1 + 5];
int f[max1 + 5], g[max1 + 5][max1 + 5][2], h[max1 + 5];
int tmpf, tmpg[max1 + 5][2], tmph;
int ans;
void Dfs ( int now, int fa, int x )
{
size[now] = 0;
f[now] = 1;
h[now] = 0;
for ( auto v : edge[now] )
{
if ( v == fa )
continue;
Dfs(v, now, x);
}
for ( auto v : edge[now] )
{
if ( v == fa )
continue;
tmpf = f[now];
memcpy(tmpg + 1, g[now] + 1, sizeof(long long) * size[now]);
tmph = h[now];
f[now] = 0;
memset(g[now] + 1, 0, sizeof(long long) * (size[now] + size[v]));
h[now] = 0;
f[now] = 1LL * tmpf * f[v] % mod * C[size[now] + size[v]][size[now]] % mod;
for ( int i = 1; i <= size[now]; i ++ )
for ( int j = 0; j <= size[v]; j ++ )
for ( int k = 0; k < 2; k ++ )
g[now][i + j][k] = (g[now][i + j][k] + 1LL * tmpg[i][k] * f[v] % mod * C[i + j - 1][i - 1] % mod * C[size[now] - i + size[v] - j][size[now] - i]) % mod;
for ( int i = 1; i <= size[v]; i ++ )
for ( int j = 0; j <= size[now]; j ++ )
for ( int k = 0; k < 2; k ++ )
g[now][i + j][k] = (g[now][i + j][k] + 1LL * g[v][i][k] * tmpf % mod * C[i + j - 1][i - 1] % mod * C[size[v] - i + size[now] - j][size[v] - i]) % mod;
for ( int i = 1; i <= size[now]; i ++ )
for ( int j = 1; j <= size[v]; j ++ )
for ( int k = 0; k < 2; k ++ )
h[now] = (h[now] + 2LL * tmpg[i][k] * g[v][j][k ^ 1] % mod * C[i + j - 2][i - 1] % mod * C[size[now] - i + size[v] - j][size[now] - i]) % mod;
if ( size[now] )
h[now] = (h[now] + 1LL * tmph * f[v] % mod * C[size[now] + size[v] - 1][size[now] - 1]) % mod;
h[now] = (h[now] + 1LL * h[v] * tmpf % mod * C[size[now] + size[v] - 1][size[v] - 1]) % mod;
size[now] += size[v];
}
if ( size[now] )
h[now] = (h[now] + g[now][1][now <= x]) % mod;
for ( int i = size[now] + 1; i >= 2; i -- )
for ( int k = 0; k < 2; k ++ )
g[now][i][k] = g[now][i - 1][k];
g[now][1][now > x] = f[now];
g[now][1][now <= x] = 0;
++size[now];
return;
}
int main ()
{
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
scanf("%d%d", &n, &root);
for ( int i = 2, u, v; i <= n; i ++ )
{
scanf("%d%d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
C[0][0] = 1;
for ( int i = 1; i <= n; i ++ )
{
C[i][0] = 1;
for ( int j = 1; j <= i; j ++ )
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
for ( int x = 1; x <= n - 1; x ++ )
{
Dfs(root, 0, x);
ans = (ans + h[root]) % mod;
}
printf("%d\n", ans);
return 0;
}
T3 数论
容易发现
只需要满足
设
简单推导发现
code
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int max1 = 5e5;
const int mod = 998244353, gmod = 3;
int n, t, m, p[max1 + 5];
int rev[max1 * 4 + 5], f[max1 * 4 + 5], g[max1 * 4 + 5];
void Add ( int &x, int y )
{
x += y;
if ( x >= mod )
x -= mod;
return;
}
int Quick_Power ( int base, int p )
{
int res = 1;
while ( p )
{
if ( p & 1 )
res = 1LL * res * base % mod;
p >>= 1;
base = 1LL * base * base % mod;
}
return res;
}
void Iterative_NTT ( int A[], int type, int len )
{
for ( int i = 0; i < len; i ++ )
if ( i < rev[i] )
swap(A[i], A[rev[i]]);
for ( int mid = 2; mid <= len; mid <<= 1 )
{
int wn = Quick_Power(gmod, (mod - 1) / mid);
if ( type == -1 )
wn = Quick_Power(wn, mod - 2);
for ( int i = 0; i < len; i += mid )
{
int w = 1;
for ( int k = 0; k < (mid >> 1); k ++ )
{
int x = A[i + k], y = 1LL * w * A[i + k + (mid >> 1)] % mod;
A[i + k] = (x + y) % mod;
A[i + k + (mid >> 1)] = (x - y + mod) % mod;
w = 1LL * w * wn % mod;
}
}
}
if ( type == -1 )
{
int inv = Quick_Power(len, mod - 2);
for ( int i = 0; i < len; i ++ )
A[i] = 1LL * A[i] * inv % mod;
}
return;
}
struct Poly
{
vector <int> F; int len;
void Zero ()
{ F.resize(len = 1); F[0] = 0; return; }
void Adjust ( int x )
{ len = min(len, x); F.resize(len); return; }
Poly operator + ( const Poly &A ) const
{
Poly res; res.len = max(len, A.len);
res.F.resize(res.len); fill(res.F.begin(), res.F.end(), 0);
for ( int i = 0; i < len; i ++ )
Add(res.F[i], F[i]);
for ( int i = 0; i < A.len; i ++ )
Add(res.F[i], A.F[i]);
return res;
}
Poly operator - ( const Poly &A ) const
{
Poly res; res.len = max(len, A.len);
res.F.resize(res.len); fill(res.F.begin(), res.F.end(), 0);
for ( int i = 0; i < len; i ++ )
Add(res.F[i], F[i]);
for ( int i = 0; i < A.len; i ++ )
Add(res.F[i], mod - A.F[i]);
return res;
}
Poly operator * ( const Poly &A ) const
{
Poly res; res.len = len + A.len - 1;
res.F.resize(res.len);
int total = 2, bit = 1;
while ( total < res.len )
total <<= 1, ++bit;
rev[0] = 0;
for ( int i = 1; i < total; i ++ )
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
memset(f, 0, sizeof(int) * total); memset(g, 0, sizeof(int) * total);
copy(F.begin(), F.end(), f), copy(A.F.begin(), A.F.end(), g);
Iterative_NTT(f, 1, total), Iterative_NTT(g, 1, total);
for ( int i = 0; i < total; i ++ )
f[i] = 1LL * f[i] * g[i] % mod;
Iterative_NTT(f, -1, total);
copy(f, f + res.len, res.F.begin());
return res;
}
};
Poly Power ( const Poly &A, int k )
{
Poly res; res.len = (A.len - 1) * k + 1;
res.F.resize(res.len);
int total = 2, bit = 1;
while ( total < res.len )
total <<= 1, ++bit;
rev[0] = 0;
for ( int i = 1; i < total; i ++ )
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
memset(f, 0, sizeof(int) * total);
copy(A.F.begin(), A.F.end(), f);
Iterative_NTT(f, 1, total);
for ( int i = 0; i < total; i ++ )
f[i] = Quick_Power(f[i], k);
Iterative_NTT(f, -1, total);
copy(f, f + res.len, res.F.begin());
return res;
}
int Bostan_Mori ( Poly P, Poly Q, int n )
{
Poly tmp;
while ( n )
{
tmp = Q;
for ( int i = 0; i < tmp.len; i ++ )
if ( i & 1 )
tmp.F[i] = mod - tmp.F[i];
P = P * tmp, Q = Q * tmp;
if ( n & 1 )
{
P.Adjust(P.len >> 1);
for ( int i = 0; i < P.len; i ++ )
P.F[i] = P.F[i << 1 | 1];
}
else
{
P.Adjust((P.len + 1) >> 1);
for ( int i = 0; i < P.len; i ++ )
P.F[i] = P.F[i << 1];
}
Q.Adjust((Q.len + 1) >> 1);
for ( int i = 0; i < Q.len; i ++ )
Q.F[i] = Q.F[i << 1];
n >>= 1;
}
return 1LL * P.F[0] * Quick_Power(Q.F[0], mod - 2) % mod;
}
Poly A[max1 + 5], B[max1 + 5], P, Q;
Poly Solve ( int L, int R, const Poly *F )
{
if ( L == R )
return F[L];
int mid = (L + R) >> 1;
return Solve(L, mid, F) * Solve(mid + 1, R, F);
}
int main ()
{
freopen("math.in", "r", stdin);
freopen("math.out", "w", stdout);
scanf("%d%d%d", &n, &t, &m);
for ( int i = 1; i <= t; i ++ )
{
scanf("%d", &p[i]);
A[i].F.resize(A[i].len = 2);
A[i].F[0] = 1; A[i].F[1] = mod - 1;
B[i].F.resize(B[i].len = 2);
B[i].F[0] = 1; B[i].F[1] = mod - p[i];
}
P = Power(Solve(1, t, A), m);
Q = Power(Solve(1, t, B), m);
printf("%d\n", Bostan_Mori(P, Q, n));
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】