2023冲刺国赛自测 9.0

T1 字符串

正解做法是将原问题转化为两棵树点集求交,直接上扫描线。

考场上尝试使用 SAM 解决,由于 SAM 的 parent 树是一个压缩后缀字典树,因此考虑将询问插入到这个字典树中,比较显然的方法是直接树剖,通过二分 + 哈希可以 O(logn) 跳一个重链,总复杂度可以做到 O(nlog2n)

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 计树

考虑枚举一个值 x ,将 P 序列中小于等于 x 的数变成 0 ,大于等于 x 的数变成 1 ,此时我们统计所有合法序列中相邻的 0110 的个数。

不难发现对于每种合法序列 PPiPi+1 的贡献会被恰好统计 |PiPi+1| 次。

考虑 dp 求解,设 fi 表示以 i 为根的子树内所有合法的 P 序列个数, gi,k,0/1 表示以 i 为根的子树中满足第 k 个位置为 0/1 的合法 P 序列个数, hi 表示以 i 为根的子树中所有合法的 P 序列中相邻的 0110 的个数。(这里定义的 P 序列不是题目中长度为 n 的序列,而是长度为 sizei 的序列,也就是题目中序列的子序列)

类似树形背包进行转移。

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 数论

容易发现 F(n) 为积性函数,因此将 F(n) 拆分为 F(piki) 统计答案,设 j=1mci,j=ki ,容易发现:

F(piki)=j=1m([ci,j=0]+[ci,j>0]×(pi1)pici,j1)

只需要满足 i=1tj=1mci,j=N ,那么答案为:

i=1tj=1m([ci,j=0]+[ci,j>0]×(pi1)pici,j1)

Hi=1+n=1(pi1)pin1xn ,不难发现答案为 [xn]i=1tHim

简单推导发现 Hi(x)=1x1pix ,直接使用波斯坦-茉莉算法求解分式第 n 项即可。

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;
}
posted @   KafuuChinocpp  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示