胡测8 (by Houraisan_Kaguya)
T1 天使玩偶/SJY摆烂
题解思路是一种类似操作分块的东西,由于 \(n\times m\times k\le 1e5\) ,因此我们可以建立一张大小为 \(1e5\) 的图,使用 Bfs 可以在 \(O(nmk)\) 的复杂度内找到每个询问点到最近的插入点的距离,于是我们对操作分块,不在同一个块内的操作,可以 Bfs 预处理,在同一个块内的操作,直接暴力枚举取 \(\min\) 即可,复杂度 \(O(nmk\sqrt{q})\) 。
简单叙述一下考试中的思路,由于 \(nmk\le 1e5\) ,一种很自然的想法是拿出范围最小的一维暴力枚举,考虑剩余两维的限制,一个自然的想法是使用二维树状数组,但此时我们有 \(O(q)\) 次插入, \(O(q(nmk)^{\tfrac{1}{3}})\) 次查询,因此考虑平衡复杂度,对于较大的一维我们仍然使用树状数组,对于较小的一维,发现大小不超过 \(O(\sqrt{nmk})\) ,因此我们直接暴力。
具体的,仍然维护一个二维数组,较大的一维用树状数组的形式维护,较小的一维直接暴力枚举修改,可以做到修改 \(O(\sqrt{nmk}\log(nmk))\) ,查询 \(O(\log(nmk))\) 。
code
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
#include <iostream>
using namespace std;
const int max1 = 1e5;
const int inf = 0x3f3f3f3f;
int n, m, h, q;
struct Question
{ int opt, x, y, z; } qus[max1 + 5];
int ans[max1 + 5];
void Pre_Work ()
{
if ( n <= min(m, h) )
{
if ( m >= h )
{
swap(m, h);
for ( int i = 1; i <= q; i ++ )
swap(qus[i].y, qus[i].z);
}
}
else if ( m <= min(n, h) )
{
if ( n <= h )
{
swap(n, m);
for ( int i = 1; i <= q; i ++ )
swap(qus[i].x, qus[i].y);
}
else
{
swap(n, m);
swap(m, h);
for ( int i = 1; i <= q; i ++ )
swap(qus[i].x, qus[i].y), swap(qus[i].y, qus[i].z);
}
}
else
{
if ( n <= m )
{
swap(m, h);
swap(n, m);
for ( int i = 1; i <= q; i ++ )
swap(qus[i].y, qus[i].z), swap(qus[i].x, qus[i].y);
}
else
{
swap(n, h);
for ( int i = 1; i <= q; i ++ )
swap(qus[i].x, qus[i].z);
}
}
return;
}
#define lowbit(now) ( now & -now )
struct Bit1
{
vector <int> tree[max1 + 5];
int high, wide;
void Clear ( int h, int w )
{
high = h;
wide = w;
for ( int i = 1; i <= h; i ++ )
{
tree[i].resize(w + 1);
for ( int k = 1; k <= w; k ++ )
tree[i][k] = inf;
}
return;
}
void Insert ( int now, int pos, int val )
{
for ( int i = now; i <= high; i += lowbit(i) )
for ( int k = pos; k <= wide; k ++ )
tree[i][k] = min(tree[i][k], val);
return;
}
int Query ( int now, int pos )
{
int res = inf;
for ( int i = now; i; i -= lowbit(i) )
res = min(res, tree[i][pos]);
return res;
}
}Tree1;
struct Bit2
{
vector <int> tree[max1 + 5];
int high, wide;
void Clear ( int h, int w )
{
high = h;
wide = w;
for ( int i = 1; i <= h; i ++ )
{
tree[i].resize(w + 1);
for ( int k = 1; k <= w; k ++ )
tree[i][k] = inf;
}
return;
}
void Insert ( int now, int pos, int val )
{
for ( int i = now; i <= high; i += lowbit(i) )
for ( int k = pos; k >= 1; k -- )
tree[i][k] = min(tree[i][k], val);
return;
}
int Query ( int now, int pos )
{
int res = inf;
for ( int i = now; i; i -= lowbit(i) )
res = min(res, tree[i][pos]);
return res;
}
}Tree2;
struct Bit3
{
vector <int> tree[max1 + 5];
int high, wide;
void Clear ( int h, int w )
{
high = h;
wide = w;
for ( int i = 1; i <= h; i ++ )
{
tree[i].resize(w + 1);
for ( int k = 1; k <= w; k ++ )
tree[i][k] = inf;
}
return;
}
void Insert ( int now, int pos, int val )
{
for ( int i = now; i; i -= lowbit(i) )
for ( int k = pos; k >= 1; k -- )
tree[i][k] = min(tree[i][k], val);
return;
}
int Query ( int now, int pos )
{
int res = inf;
for ( int i = now; i <= high; i += lowbit(i) )
res = min(res, tree[i][pos]);
return res;
}
}Tree3;
struct Bit4
{
vector <int> tree[max1 + 5];
int high, wide;
void Clear ( int h, int w )
{
high = h;
wide = w;
for ( int i = 1; i <= h; i ++ )
{
tree[i].resize(w + 1);
for ( int k = 1; k <= w; k ++ )
tree[i][k] = inf;
}
return;
}
void Insert ( int now, int pos, int val )
{
for ( int i = now; i; i -= lowbit(i) )
for ( int k = pos; k <= wide; k ++ )
tree[i][k] = min(tree[i][k], val);
return;
}
int Query ( int now, int pos )
{
int res = inf;
for ( int i = now; i <= high; i += lowbit(i) )
res = min(res, tree[i][pos]);
return res;
}
}Tree4;
void Solve ( int num )
{
Tree1.Clear(h, m);
Tree2.Clear(h, m);
Tree3.Clear(h, m);
Tree4.Clear(h, m);
for ( int i = 1; i <= q; i ++ )
{
if ( qus[i].opt == 1 )
{
if ( qus[i].x == num )
{
Tree1.Insert(qus[i].z, qus[i].y, -qus[i].z - qus[i].y);
Tree2.Insert(qus[i].z, qus[i].y, -qus[i].z + qus[i].y);
Tree3.Insert(qus[i].z, qus[i].y, qus[i].z + qus[i].y);
Tree4.Insert(qus[i].z, qus[i].y, qus[i].z - qus[i].y);
}
}
else
{
int tmp = abs(qus[i].x - num);
ans[i] = min(ans[i], tmp + qus[i].z + qus[i].y + Tree1.Query(qus[i].z, qus[i].y));
ans[i] = min(ans[i], tmp + qus[i].z - qus[i].y + Tree2.Query(qus[i].z, qus[i].y));
ans[i] = min(ans[i], tmp - qus[i].z - qus[i].y + Tree3.Query(qus[i].z, qus[i].y));
ans[i] = min(ans[i], tmp - qus[i].z + qus[i].y + Tree4.Query(qus[i].z, qus[i].y));
}
}
return;
}
int main ()
{
freopen("sjy.in", "r", stdin);
freopen("sjy.out", "w", stdout);
scanf("%d%d%d%d", &n, &m, &h, &q);
for ( int i = 1; i <= q; i ++ )
scanf("%d%d%d%d", &qus[i].opt, &qus[i].x, &qus[i].y, &qus[i].z);
Pre_Work();
for ( int i = 1; i <= q; i ++ )
ans[i] = inf;
for ( int i = 1; i <= n; i ++ )
Solve(i);
for ( int i = 1; i <= q; i ++ )
if ( qus[i].opt == 2 )
printf("%d\n", ans[i]);
return 0;
}
T2 Koishi的树
首先建立 AC 自动机,于是很容易有一个暴力的思路,设 \(f_{u,i,0/1}\) 表示当前在原树 \(u\) 节点,在 AC 自动机 \(i\) 节点,是否匹配过一个完整的串的方案数。简单进行优化,容易发现可以预处理每条边的转移矩阵,并使用树剖线段树维护,由于查询时的初始矩阵是一个向量,因此可以做到 \(O(nm^3)\) 预处理, \(O(qm^2\log^2n)\) 查询。
code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int max1 = 1e4, max2 = 35, B = 15;
const int mod = 998244353;
int n, m, q;
struct Node
{ int next, v, len; char str[max2 + 5]; } edge[max1 * 2 + 5];
int head[max1 + 5], total, prev_edge[max1 + 5];
int siz[max1 + 5], deep[max1 + 5], father[max1 + 5], son[max1 + 5];
int top[max1 + 5], dfn[max1 + 5], rk[max1 + 5], dfs_clock;
void Add ( int u, int v, const char *s, int len )
{
edge[++total].v = v;
edge[total].len = len;
for ( int i = 1; i <= len; i ++ )
edge[total].str[i] = s[i];
edge[total].next = head[u];
head[u] = total;
return;
}
void Find_Heavy_Edge ( int now, int fa, int depth )
{
siz[now] = 1, deep[now] = depth, father[now] = fa, son[now] = 0;
int max_siz = 0;
for ( int i = head[now]; i; i = edge[i].next )
{
int v = edge[i].v;
if ( v == fa )
{ prev_edge[now] = i; continue; }
Find_Heavy_Edge(v, now, depth + 1);
if ( max_siz < siz[v] )
max_siz = siz[v], son[now] = v;
siz[now] += siz[v];
}
return;
}
void Connect_Heavy_Edge ( int now, int ancestor )
{
top[now] = ancestor;
dfn[now] = ++dfs_clock;
rk[dfs_clock] = now;
if ( son[now] )
Connect_Heavy_Edge(son[now], ancestor);
for ( int i = head[now]; i; i = edge[i].next )
{
int v = edge[i].v;
if ( v == father[now] || v == son[now] )
continue;
Connect_Heavy_Edge(v, v);
}
return;
}
struct Aho_Corasick_Automation
{
int trans[max2 + 5][26], fail[max2 + 5], total;
bool tag[max2 + 5];
int que[max2 + 5], L, R;
void Clear ()
{
memset(trans[0], 0, sizeof(trans[0]));
fail[0] = -1; tag[0] = false; total = 0;
return;
}
void Insert ( const char *s, int len )
{
int now = 0;
for ( int i = 1; i <= len; i ++ )
{
if ( !trans[now][s[i] - 'a'] )
{
trans[now][s[i] - 'a'] = ++total;
memset(trans[total], 0, sizeof(trans[total]));
fail[total] = 0;
tag[total] = false;
}
now = trans[now][s[i] - 'a'];
}
tag[now] = true;
return;
}
void Build ()
{
L = 1, R = 0;
for ( int i = 0; i < 26; i ++ )
{
if ( trans[0][i] )
{
fail[trans[0][i]] = 0;
que[++R] = trans[0][i];
}
}
while ( L <= R )
{
int x = que[L++];
for ( int i = 0; i < 26; i ++ )
{
if ( !trans[x][i] )
trans[x][i] = trans[fail[x]][i];
else
{
fail[trans[x][i]] = trans[fail[x]][i];
que[++R] = trans[x][i];
}
}
}
return;
}
}AC;
struct Vector
{
int matrix[max2 + 5];
void Clear ()
{
for ( int i = 0; i <= AC.total + 1; i ++ )
matrix[i] = 0;
return;
}
};
struct Matrix
{
int matrix[max2 + 5][max2 + 5];
void Clear ()
{
for ( int i = 0; i <= AC.total + 1; i ++ )
for ( int j = 0; j <= AC.total + 1; j ++ )
matrix[i][j] = 0;
return;
}
Matrix operator * ( const Matrix &A ) const
{
Matrix res;
unsigned long long tmp;
for ( int i = 0; i <= AC.total + 1; i ++ )
{
for ( int j = 0; j <= AC.total + 1; j ++ )
{
tmp = 0;
for ( int k = 0; k <= min(B, AC.total + 1); k ++ )
tmp += 1uLL * matrix[i][k] * A.matrix[k][j];
tmp %= mod;
for ( int k = B + 1; k <= min(B + B, AC.total + 1); k ++ )
tmp += 1uLL * matrix[i][k] * A.matrix[k][j];
tmp %= mod;
for ( int k = B + B + 1; k <= AC.total + 1; k ++ )
tmp += 1uLL * matrix[i][k] * A.matrix[k][j];
tmp %= mod;
res.matrix[i][j] = tmp;
}
}
return res;
}
Vector operator * ( const Vector &A ) const
{
Vector res;
unsigned long long tmp;
for ( int i = 0; i <= AC.total + 1; i ++ )
{
tmp = 0;
for ( int k = 0; k <= min(B, AC.total + 1); k ++ )
tmp += 1uLL * matrix[i][k] * A.matrix[k];
tmp %= mod;
for ( int k = B + 1; k <= min(B + B, AC.total + 1); k ++ )
tmp += 1uLL * matrix[i][k] * A.matrix[k];
tmp %= mod;
for ( int k = B + B + 1; k <= AC.total + 1; k ++ )
tmp += 1uLL * matrix[i][k] * A.matrix[k];
tmp %= mod;
res.matrix[i] = tmp;
}
return res;
}
};
struct Segment_Tree
{
#define lson(now) ( now << 1 )
#define rson(now) ( now << 1 | 1 )
struct Data
{ Matrix prodL, prodR; int same; } tree[max1 * 4 + 5];
void Push_Up ( int now )
{
tree[now].same = 0;
if ( tree[lson(now)].same && tree[rson(now)].same && tree[lson(now)].same == tree[rson(now)].same )
{
tree[now].same = tree[lson(now)].same;
tree[now].prodL = tree[rson(now)].prodL * tree[lson(now)].prodL;
tree[now].prodR = tree[lson(now)].prodR * tree[rson(now)].prodR;
}
return;
}
void Build ( int now, int L, int R )
{
if ( L == R )
{
int p = rk[L];
tree[now].same = top[p];
tree[now].prodL.Clear();
tree[now].prodR.Clear();
if ( p != 1 )
{
p = prev_edge[p];
for ( int i = 0; i <= AC.total + 1; i ++ )
{
for ( int k = 1; k <= edge[p].len; k ++ )
{
int v;
if ( i == AC.total + 1 )
v = AC.total + 1;
else
{
v = AC.trans[i][edge[p].str[k] - 'a'];
if ( AC.tag[v] )
v = AC.total + 1;
}
++tree[now].prodL.matrix[v][i];
++tree[now].prodR.matrix[v][i];
}
}
}
return;
}
int mid = L + R >> 1;
Build(lson(now), L, mid);
Build(rson(now), mid + 1, R);
Push_Up(now);
return;
}
void QueryL ( int now, int L, int R, int ql, int qr, Vector &v )
{
if ( L >= ql && R <= qr )
{ v = tree[now].prodL * v; return; }
int mid = L + R >> 1;
if ( ql <= mid )
QueryL(lson(now), L, mid, ql, qr, v);
if ( qr > mid )
QueryL(rson(now), mid + 1, R, ql, qr, v);
return;
}
void QueryR ( int now, int L, int R, int ql, int qr, Vector &v )
{
if ( L >= ql && R <= qr )
{ v = tree[now].prodR * v; return; }
int mid = L + R >> 1;
if ( qr > mid )
QueryR(rson(now), mid + 1, R, ql, qr, v);
if ( ql <= mid )
QueryR(lson(now), L, mid, ql, qr, v);
return;
}
}Tree;
struct Question
{
int L, R;
Question () {}
Question ( int __L, int __R )
{ L = __L, R = __R; }
}tmp1[max2 + 5], tmp2[max2 + 5];
int len1, len2;
int Query ( int u, int v )
{
len1 = len2 = 0;
while ( top[u] != top[v] )
{
if ( deep[top[u]] > deep[top[v]] )
{
tmp1[++len1] = Question(dfn[top[u]], dfn[u]);
u = father[top[u]];
}
else
{
tmp2[++len2] = Question(dfn[top[v]], dfn[v]);
v = father[top[v]];
}
}
if ( u != v )
{
if ( deep[u] > deep[v] )
tmp1[++len1] = Question(dfn[v] + 1, dfn[u]);
else
tmp2[++len2] = Question(dfn[u] + 1, dfn[v]);
}
Vector ans;
ans.Clear();
ans.matrix[0] = 1;
for ( int i = 1; i <= len1; i ++ )
Tree.QueryR(1, 1, n, tmp1[i].L, tmp1[i].R, ans);
for ( int i = len2; i >= 1; i -- )
Tree.QueryL(1, 1, n, tmp2[i].L, tmp2[i].R, ans);
return ans.matrix[AC.total + 1];
}
int main ()
{
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
char tmp[max2 + 5]; int len;
AC.Clear();
scanf("%d%d%d", &n, &m, &q);
for ( int i = 2, u, v; i <= n; i ++ )
{
scanf("%d%d%s", &u, &v, tmp + 1);
len = strlen(tmp + 1);
Add(u, v, tmp, len);
Add(v, u, tmp, len);
}
for ( int i = 1; i <= m; i ++ )
{
scanf("%s", tmp + 1);
len = strlen(tmp + 1);
AC.Insert(tmp, len);
}
AC.Build();
Find_Heavy_Edge(1, 0, 0);
Connect_Heavy_Edge(1, 1);
Tree.Build(1, 1, n);
int u, v;
while ( q -- )
{
scanf("%d%d", &u, &v);
printf("%d\n", Query(u, v));
}
return 0;
}
T3 APjifengc 的 Galgame
怎么可以存在只有剧情没有 CG 的 galgame 呢!
首先存在一种暴力 dp ,设 \(f_{u,i}\) 表示在 \(u\) 节点,任意结局到当前节点的 CG 个数为 \(i\) 时的方案数,转移显然有 \(f_{u,i}=\prod_v f_{v,i}+\prod_v f_{v,i-1}\) ,简单进行优化,当一个节点存在 \(2\) 个及以上的儿子时,暴力合并复杂度为最小的儿子的深度,复杂度正确,当一个节点只存在 \(1\) 个儿子时,可以直接继承并维护一种懒惰标记表示真实多项式是当前多项式 \(\times (1+x)^t\) ,暴力合并时使用 NTT 下放标记即可。
code
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int max1 = 1e5;
const int mod = 998244353, gmod = 3;
const int inf = 0x3f3f3f3f;
void Add ( int &x, int y )
{
x = x + y;
if ( x >= mod )
x = 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;
}
int bit, total, rev[max1 * 4 + 5], f[max1 * 4 + 5], g[max1 * 4 + 5];
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 + ( mid >> 1 ) + k] % mod;
A[i + k] = x + y;
A[i + ( mid >> 1 ) + k] = x - y + mod;
if ( A[i + k] >= mod )
A[i + k] -= mod;
if ( A[i + ( mid >> 1 ) + k] >= mod )
A[i + ( mid >> 1 ) + k] -= mod;
w = 1LL * w * wn % mod;
}
}
}
return;
}
struct Poly
{
vector <int> F; int len;
void Adjust ( int target )
{
if ( len > target )
{ len = target; F.resize(len); }
return;
}
Poly operator * ( const Poly &A ) const
{
Poly res;
res.len = len + A.len - 1;
res.F.resize(res.len);
if ( A.len <= 40 )
{
for ( int i = 0; i < res.len; i ++ )
res.F[i] = 0;
for ( int i = 0; i < len; i ++ )
for ( int j = 0; j < A.len; j ++ )
res.F[i + j] = ( res.F[i + j] + 1LL * F[i] * A.F[j] ) % mod;
}
else
{
bit = 1, total = 2;
while ( total < res.len )
total <<= 1, ++bit;
for ( int i = 0; i < total; i ++ )
f[i] = g[i] = 0;
rev[0] = 0;
for ( int i = 1; i < total; i ++ )
rev[i] = rev[i >> 1] >> 1 | ( i & 1 ) << bit - 1;
for ( int i = 0; i < len; i ++ )
f[i] = F[i];
for ( int i = 0; i < A.len; i ++ )
g[i] = A.F[i];
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);
int P = Quick_Power(total, mod - 2);
for ( int i = 0; i < res.len; i ++ )
res.F[i] = 1LL * f[i] * P % mod;
}
return res;
}
};
Poly A[max1 + 5], tmp;
int inv[max1 + 5], fac[max1 + 5], ifac[max1 + 5];
int n, son[max1 + 5], mindeep[max1 + 5], num[max1 + 5], seq[max1 + 5];
vector <int> edge[max1 + 5];
int C ( int n, int m )
{
if ( n < m || n < 0 || m < 0 )
return 0;
return 1LL * fac[n] * ifac[n - m] % mod * ifac[m] % mod;
}
void Solve ( int now, int lim )
{
A[num[now]].Adjust(lim + 1);
tmp.len = min(seq[now], lim) + 1;
tmp.F.resize(tmp.len);
for ( int i = 0; i < tmp.len; i ++ )
tmp.F[i] = C(seq[now], i);
A[now] = A[num[now]] * tmp;
A[now].Adjust(lim + 1);
return;
}
void Dfs ( int now, int fa )
{
son[now] = 0;
mindeep[now] = inf;
for ( auto v : edge[now] )
{
if ( v == fa )
continue;
Dfs(v, now);
++son[now];
mindeep[now] = min(mindeep[now], mindeep[v] + 1);
}
if ( !son[now] )
{
mindeep[now] = 1;
num[now] = now;
seq[now] = 0;
A[now].len = 2;
A[now].F.resize(2);
A[now].F[0] = A[now].F[1] = 1;
}
else if ( son[now] == 1 )
{
for ( auto v : edge[now] )
{
if ( v == fa )
continue;
num[now] = num[v];
seq[now] = seq[v] + 1;
}
}
else
{
num[now] = now;
seq[now] = 0;
A[now].len = mindeep[now] + 1;
A[now].F.resize(mindeep[now] + 1);
for ( int i = 0; i < mindeep[now]; i ++ )
A[now].F[i] = 1;
A[now].F[mindeep[now]] = 0;
for ( auto v : edge[now] )
{
if ( v == fa )
continue;
Solve(v, mindeep[now] - 1);
for ( int i = 0; i < mindeep[now]; i ++ )
A[now].F[i] = 1LL * A[now].F[i] * A[v].F[i] % mod;
}
for ( int i = mindeep[now]; i >= 1; i -- )
Add(A[now].F[i], A[now].F[i - 1]);
}
return;
}
int main ()
{
freopen("gal.in", "r", stdin);
freopen("gal.out", "w", stdout);
scanf("%d", &n);
inv[1] = 1;
for ( int i = 2; i <= n; i ++ )
inv[i] = 1LL * ( mod - mod / i ) * inv[mod % i] % mod;
fac[0] = 1, ifac[0] = inv[1];
for ( int i = 1; i <= n; i ++ )
fac[i] = 1LL * fac[i - 1] * i % mod,
ifac[i] = 1LL * ifac[i - 1] * inv[i] % mod;
for ( int i = 2, u, v; i <= n; i ++ )
{
scanf("%d%d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
Dfs(1, 0);
Solve(1, mindeep[1]);
int ans = 0;
for ( int i = 0; i < A[1].len; i ++ )
Add(ans, A[1].F[i]);
printf("%d\n", ans);
return 0;
}

浙公网安备 33010602011771号