中山纪念中学 Day 4 2019.08.04 解题报告 & 题解

T1 锻造 Forging

1 记录

考场上的时候总觉得题目非常的奇妙

因为一直循环下去不就完了吗?

直到后来我的同桌给我指点,原来把 DP 式子当方程解可以了

还是太菜啊

2 Solution

显然,我们设 $ f[i] $ 为到第 $i$ 级的期望的话,则有


$$
f[i] = f[i – 2] + f[i – 1] + (1 – p) * (f[i] – f[i – 2])
$$
整理得
$$
f[i] = f[i – 2] + \frac{f[i – 1]}{p}
$$
没了

对了,线性求逆元才能过

3 Source

#include <cstdio>

inline int Min( int a, int b ) { return a < b? a: b; } 

const int N = 1e7 + 1e2;
const int mod = 998244353;

inline int add( int a, int b ) { 
    if( a + b > mod )
        return a + b - mod;
    return a + b;
}
inline int mul( int a, int b ) { return ( 1LL* a * b ) % mod; }

int n, a, bx, by, cx, cy, p;
int b[N], c[N], f[N], inv[N];

int main() {
    freopen( "forging.in", "r", stdin );
    freopen( "forging.out", "w", stdout );

    inv[1] = 1;
    for( int i = 2; i <= (int)(1e7); i++ )
        inv[i] = mul( mod - ( mod / i ), inv[ mod % i ] );


    scanf( "%d%d", &n, &a );    
    scanf( "%d%d%d%d%d", &bx, &by, &cx, &cy, &p );

    b[0] = by + 1; c[0] = cy + 1;
    for( int i = 1; i < n; i++ ) {
        b[i] = ( 1LL * b[ i - 1 ] * bx + by ) % p + 1;
        c[i] = ( 1LL * c[ i - 1 ] * cx + cy ) % p + 1;
    }

    f[0] = a;
    f[1] = add( f[0], mul( mul( f[0], c[0] ), inv[ Min( c[0], b[0] ) ] ) );

    for( int i = 2; i <= n; i++ ) 
        f[i] = add( f[ i - 2 ], mul( mul( f[ i - 1 ], c[ i - 1 ] ), inv[ Min( c[ i - 1 ], b[ i - 2 ] ) ] ) );

    printf( "%d", f[n] );
}

T2 整除 Division

1 记录

考场上直接快速幂暴力跑路

看出来 CRT 能搞忘记 CRT 了

2 Solution

显然,本题可以转化为以下形式
$$
\begin{cases}
x ^ m \equiv x \mod p_1 \\
x ^ m \equiv x \mod p_2 \\
\cdots \\
x ^ m \equiv x \mod p_c
\end{cases}
$$
其中 $p$ 数列是题目给定的素数

将其中每一个方程的解个个数乘起来便是答案

但是直接暴力做的复杂度是 $ O(T \times \sum_{i = 1}^c p_i \times \log(m)) $

这个复杂度只有 $80\%$ 的分数,后面会 TLE

我们可以每次线性筛 $ x^m \mod p_i $

设 $ f(x) = x ^ m \mod p_i $

显然,$f$ 是完全积性函数,线性筛即可

复杂度 $ O(T \times ( \sum_{i = 1}^c p_i + k \times \log(m) )$

其中 $k$ 是 $ p_i $ 的质数个数,卡卡常就过了

值得一提的是,这道题目还有基于原根性质 $ O( T \times c \log (p)) $ 的做法,请自行查找

代码因为卡常非常丑陋,凑活看吧……

3 Source

#include <stdio.h>
#include <string.h>

inline unsigned int read() {
    register char c = 0; register unsigned int now = 0;
    while( c < '0' || c > '9' )
        c = getchar();
    while( c >= '0' && c <= '9' ) {
        now = ( now << 3 ) + ( now << 1 ) + ( c - 48 );
        c = getchar();
    }
    return now;
}

const unsigned int N = 1e4 + 3;
const unsigned int MOD = 998244353;

//inline unsigned int mul( register unsigned int a, register unsigned int b, register unsigned int mod = MOD ) { return ( 1LL * a * b ) % mod; }

unsigned int T, c, m;

inline unsigned int ksm( register unsigned int a, register unsigned int p, register unsigned int mod ) {
    register unsigned int res = 1;
    while(p) {
        if( p & 1 ) 
            res = res * a % mod;
        a = a * a % mod;
        p >>= 1;
    }
    return res;
}

unsigned int tmp_pcnt, real_pcnt;
unsigned int pri[N], f[N];
bool vis[N];
inline void pre( register unsigned int mod ) {
    tmp_pcnt = 0;
    for( register unsigned int i = 1; i <= real_pcnt; i++ )
        f[ pri[i] ] = ksm( pri[i], m, mod );

    for( register unsigned int i = 2; i < mod; i++ ) {
        if( vis[i] == false ) 
            ++ tmp_pcnt;
        for( register unsigned int j = 1; j <= tmp_pcnt; j++ ) {
            if( pri[j] * i > mod )
                break;

            f[ i * pri[j] ] = ( f[i] * f[ pri[j] ] ) % mod;

            if( i % pri[j] == 0 ) 
                break;
        }
    }
}

inline void get_pri() {
    for( register unsigned int i = 2; i <= 10000; i++ ) {
        if( vis[i] == false ) 
            pri[ ++ real_pcnt ] = i;
        for( register unsigned int j = 1; j <= real_pcnt; j++ ) {
            if( pri[j] * i > 10000 )
                break;
            vis[ i * pri[j] ] = true;
            if( i % pri[j] == 0 ) 
                break;
        }
    }
}

int main() {
    freopen( "division.in", "r", stdin );
    freopen( "division.out", "w", stdout );

    get_pri();
    f[1] = 1;

    read(); T = read();

    while( T-- ) {
        register unsigned int ans = 1, cnt;
        c = read(), m = read();

        register unsigned int tmp;
        for( register unsigned int i = 1; i <= c; i++ ) {
            tmp = read();

            pre( tmp );
            cnt = 2;
            for( register unsigned int j = 2; j < tmp; j++ ) {
                if( f[j] - j == 0 ) 
                    cnt ++;
            }
            if( cnt != 0 ) 
                ans = ( 1LL * ans * cnt ) % MOD;
        }
        printf( "%d\n", ans );
    }
}

T3 欠钱 Money

1 记录

考场胡出来了前 $ 60\% $ 的数据结果没时间调了,一分未得……

2 Solution

题目有多种解法,参考本目录下的 官方 Soltion.pdf

这里只讲述 LCT 解法

复杂度不想讲了

注意 LCT 求 LCA 的方法

access(a)access(b) 最后 splay 到的节点即为 $lca(a,b)$

后面就是 LCT 标准操作

3 Source

#pragma GCC optimize(3,"Ofast","inline")
#include <cstdio>
#include <cstring>

#include <algorithm>

inline int Min( register int a, register int b ) { return a < b? a: b; }

int read() {
    register char c = 0; register int now = 0;
    while( c < '0' || c > '9' )
        c = getchar();
    while( c >= '0' && c <= '9' ) {
        now = ( now << 3 ) + ( now << 1 ) + ( c - 48 );
        c = getchar();
    }
    return now;
}

int st[100001], stcnt;
void write( register int a, register char end = 0 ) {
    while(a) {
        st[ ++ stcnt ] = a % 10;
        a /= 10;
    }
    if( stcnt == 0 )
        putchar( '0' );
    while( stcnt ) {
        putchar( st[stcnt] + '0' );
        stcnt --;
    }
    putchar( end );
}

const int N = 1e5 + 5;
const int INF = 0x3f3f3f3f;

int n, m, last_ans;

inline void get_real( register int &a ) { a = ( a + last_ans ) % n + 1; }

//struct _fa {
    int fa[N];
    void init( register int now ) { for( register int i = 1; i <= now; i++ ) fa[i] = i; }
//    int& operator[] ( register int now ) { return fa[now]; }
    int get_fa( register int now ) {
        if( fa[now] == now ) 
            return now;
        return fa[now] = get_fa( fa[now] );
    }
//}fa;


struct node {
    int son[2], fa, val, min;
    bool rev;
} tree[N];

inline bool get_son( register int now ) { return tree[ tree[now].fa ].son[1] == now; }

inline bool not_root( register int now ) { return tree[ tree[now].fa ].son[0] == now || tree[ tree[now].fa ].son[1] == now ; }

inline void push_up( register int now ) {
    tree[now].min = tree[now].val;
    tree[now].min = Min( tree[ tree[now].son[0] ].min , Min( tree[ tree[now].son[1] ].min, tree[now].val ) );
}

inline void get_re( register int now ) {
    std::swap( tree[now].son[0], tree[now].son[1] );
    tree[now].rev ^= 1;
}

inline void push_down( register int now ) {
    if( tree[now].rev ) {
        if( tree[now].son[0] ) 
            get_re( tree[now].son[0] );
        if( tree[now].son[1] ) 
            get_re( tree[now].son[1] );
        tree[now].rev = 0;
    }
}

inline void push_all( register int now ) {
    while(now) {
        if( not_root(now) == false ) {
            push_down(now);
            break;
        }
        st[ ++ stcnt ] = now;
        now = tree[now].fa;
    }
    while( stcnt ) {
        push_down( st[stcnt] );
        stcnt --;
    }
    stcnt = 0;
//    if( not_root(now) ) 
//        push_all( tree[now].fa );
//    push_down(now);
}

inline void rotate( register int now ) {
    register int tmp = tree[now].fa;
    register bool kind = get_son(now);
    if( not_root(tmp) ) 
        tree[ tree[tmp].fa ].son[ get_son(tmp) ] = now;
    tree[now].fa = tree[tmp].fa; 
    tree[tmp].son[kind] = tree[now].son[ kind ^ 1 ]; tree[ tree[tmp].son[kind] ].fa = tmp;
    tree[tmp].fa = now; tree[now].son[ kind ^ 1 ] = tmp;
    push_up(tmp); push_up(now);
}

inline void splay( register int now ) {
    push_all(now);
    while( not_root(now) ) {
        register int tmp = tree[now].fa;
        if( not_root( tmp ) ) 
            rotate( get_son(tmp) == get_son(now)? tmp: now ); 
        rotate(now);
    }
}

inline int access( register int now ) {
    register int res = 0;
    for( register int tmp = 0; now; now = tree[ tmp = now ].fa ) 
        splay(now), tree[now].son[1] = tmp, push_up(now), res = now;
    return res;
}

inline void makeroot( register int now ) {
    access(now); splay(now); get_re(now);
}

inline void link( register int from, register int to, register int val ) {
    splay(from);
    tree[from].fa = to;
    tree[from].val = val;
    tree[from].min = Min( tree[from].min, tree[from].val );
}

inline int ask( register int from, register int to ) {
    register int res = 0;
    access(to);
    if( access(from) == to ) {
        makeroot(to);
        access(from);
        splay(to);
        res = tree[ tree[to].son[1] ].min;
        makeroot( get_fa(to) );
    }
    return res;
}

int main() {
    freopen( "money.in", "r", stdin );
    freopen( "money.out", "w", stdout );

    last_ans = 0; tree[0].min = INF;
    n = read(); m = read();
    init(n);
    for( register int i = 1; i <= m; i++ ) {
        register int op = read();
        if( op == 0 ) {
            register int a = read(), b = read(), c = read();    
            get_real(a), get_real(b), get_real(c);
            fa[a] = b;
            link( a, b, c );
        }
        else {
            register int a = read(), b = read();
            get_real(a), get_real(b);
            if( get_fa(a) != get_fa(b) ) 
                last_ans = 0;
            else
                last_ans = ask( a, b );
            write( last_ans, '\n' );
        }
    }
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

message
account_circle
Please input name.
email
Please input email address.
links

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据