中山纪念中学 Day 7 2019.08.07 解题报告 & 题解

T1 小 L 的数列

1 记录

考场上推出来了

没判 $ n < q $ 难受

2 Solution

显然,每个 $ f_i $ 都是等于下面的试子
$$
\prod_{i=1}^k f_i^{b_i}
$$
也就说,我们可以求 $b_i$

根据费马小定理
$$
a ^ {p -1 } \equiv 1 \pmod p
$$
则显然
$$
\begin{align}
a ^ {k \pm (p – 1)} & \equiv a ^ k \pmod p \\
a ^ {k \bmod (p – 1) } & \equiv a ^ k \pmod p
\end{align}
$$
即指数应当对 $ p – 1 $ 取模

显然
$$
f_{a’,b’} = \sum_{i = 1}^k f_{a’ – i,b’} \times a_i
$$
其中 $b’$ 表示求 $b_{b’}$

矩阵优化即可

3 Source

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

const int N = 210;
const int mod = 998244353;

inline 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;
}

inline int add( int a, int b, const int mod = mod ) {
    int t = a + b;
    return t >= mod? t - mod: t;
}
inline int mul( int a, int b, const int mod = mod ) {
    return ( 1LL * a * b ) % mod;
}

int n, m, ans = 1;
int b[N], f[N];

struct matrix {
    int f[N][N];
    matrix( int tmp = 0 ) {
        memset( f, 0, sizeof(f) );
        if( tmp != 0 ) {
            for( int i = 0; i <= 200; i++ ) 
                f[i][i] = tmp;
        }
    }
    int* operator[] ( int now ) { return f[now]; }
    matrix operator* ( matrix b )  {
        matrix res;
        for( int i = 1; i <= m; i++ ) {
            for( int j = 1; j <= m; j++ ) {
                for( int k = 1; k <= m; k++ ) {
                    res[i][j] = add( res[i][j], mul( this -> f[i][k], b[k][j], mod - 1 ), mod - 1 );    
                }
            }
        }
        return res;
    }
} a, t;

matrix ksm( matrix a, int p ) {
    matrix res(1);
    while(p) {
        if( p & 1 ) 
            res = res * a;
        a = a * a;
        p >>= 1;
    }
    return res;
}

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

matrix miao( matrix a, matrix t ) {
    matrix res;
    for( int i = 1; i <= m; i++ ) {
        for( int j = 1; j <= m; j++ ) {
            res[1][i] = add( res[1][i], mul( a[1][j], t[j][i] , mod - 1 ), mod - 1 );
        }
    }
    return res;
}

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

    n = read(), m = read();
    for( int i = 1; i <= m; i++ ) {
        b[i] = read();
    }
    for( int i = 1; i <= m; i++ ) {
        f[i] = read();
    }

    if( n <= m ) {
        printf( "%d", f[n] );
        return 0;
    }

    for( int i = 1; i <= m; i++ ) {
        t[i][1] = b[i]; 
        if( i != 1 ) 
            t[i - 1][i] = 1;
    }

    matrix tmp = ksm( t, n - m );
    for( int i = 1; i <= m; i++ ) {
        a[1][i] = 1;
        a[1][i - 1] = 0;
        ans = mul( ans, ksm( f[ m - i + 1 ], miao(a, tmp)[1][1] ) );
    }

    printf( "%d\n", ans );

    fclose(stdin);
    fclose(stdout);
    return 0;
}

T2 梦境

1 记录

别说了,考场上全在肝 T1

2 Solution

网上太多的,这题都出烂了

先将梦境转折点排序,然后按左端点排序所有区间,将所有梦境转折点尽可能匹配靠左的区间即可

贪心正确性显然,不做证明

3 Source

#include <cstdio>

#include <queue>
#include <algorithm>

const int N = 2e5 + 1e4;

int n, m, ans;
int t[N];

struct seq {
    int left, rig;
    bool operator< ( const seq b )const { return this -> rig > b.rig; }
} a[N];

std::priority_queue<seq> q;

bool cmp_int( int a, int b ) { return a < b; }
bool cmp_seq( seq a, seq b ) { return a.left < b.left; }

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

    scanf( "%d%d", &n, &m );
    for( int i = 1; i <= n; i++ ) {
        scanf( "%d%d", &a[i].left, &a[i].rig );
    }
    for( int i = 1; i <= m; i++ ) {
        scanf( "%d", &t[i] );
    }

    std::sort( a + 1, a + n + 1, cmp_seq );
    std::sort( t + 1, t + m + 1, cmp_int );

    int p = 1;
    for( int i = 1; i <= m; i++ ) {
        while( a[p].left <= t[i] && p <= n )
            q.push( a[p] ), p ++;
        while( !q.empty() ) {
            seq tmp = q.top();
            q.pop();
            if( tmp.left <= t[i] && tmp.rig >= t[i] ) {
                ans ++;
                break;
            }
        }
    }

    printf( "%d", ans );

    fclose(stdin);
    fclose(stdout);
    return 0;
}

T3 树

1 记录

不说了

2 Solution

这东西我调了一整天

先想其在一条链上,我们将问题直接转化到序列上

从 $[1,l]$中 到 $[r,n]$中 显然是不可达的

我们可以把的转化到二维平面上,即 $x$ 坐标 $[1,l]$ ,$y$ 坐标 $[r,n]$ 的矩形

然后依次添加,就是一道裸的矩形面积并问题,扫描线就行

但是在树上呢?

先用 DFN 序转化到数列上

若 $(x,y)$ 不是父子关系照上

若 $(x,y)$ 为父子关系,$x$ 为父亲的情况下, $y$ 的子树不可到达整棵树除 $x$ 包含 $y$ 的子树的子树

然后但序列上的状况来做即可

3 Source

#include <cstdio>

#include <algorithm>

inline 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;
}

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

const int N = 1e5 + 10;

int n, m, scnt;
long long ans;

// Edge Start 
struct edge {
    int to, next;
} e[N << 2];
int ehead[N << 1], ecnt;
inline void add_edge( int now, int to ) {
    ecnt ++;
    e[ecnt].to = to;
    e[ecnt].next = ehead[now];
    ehead[now] = ecnt;
}
// Edge End

// Segment Tree Start 
int tree[N << 2], lazy[N << 2];

inline void calc( int now, int left, int rig ) {
    if( lazy[now] ) 
        tree[now] = ( rig - left + 1 );
    else if( rig - left == 0 ) 
        tree[now] = 0;
    else 
        tree[now] = tree[ now << 1 ] + tree[ now << 1 | 1 ];
}

inline void pushup( int now, int left, int rig ) {
    if( lazy[now << 1] && lazy[ now << 1 | 1 ] ) {
        int tmp = Min( lazy[now << 1], lazy[ now << 1 | 1 ] );
        lazy[now] = tmp;
        lazy[ now << 1 ] -= tmp; 
        lazy[ now << 1 | 1 ] -= tmp; 
        int mid = ( left + rig ) >> 1;
        calc( now << 1, left, mid );
        calc( now << 1 | 1, mid + 1, rig );
    }
}

inline void pushdown( int now, int left, int rig ) {
    if( lazy[now] ) {
        int mid = ( left + rig ) >> 1;
        lazy[ now << 1 ] += lazy[now];
        lazy[ now << 1 | 1 ] += lazy[now];
        calc( now << 1, left, mid );
        calc( now << 1 | 1, mid + 1, rig );
        lazy[now] = 0;
        calc( now, left, rig );
    }
}

void query_add( int from, int to, int val, int now, int left, int rig ) {
    if( from <= left && rig <= to ) {
        lazy[now] += val;
        calc( now, left, rig );
        return ;
    }
    int mid = ( left + rig ) >> 1;
    pushdown( now, left, rig );
    if( from <= mid )
        query_add( from, to, val, now << 1, left, mid );
    if( to > mid )
        query_add( from, to, val, now << 1 | 1, mid + 1, rig );
    pushup( now, left, rig );
    calc( now, left, rig );
}
// Segment Tree End

// Heavy-Light Decompostion Start
int fa[N], fir[N], last[N], son[N], mson[N], top[N], cnt;
void dfs1( int now ) {
    fir[now] = ++ cnt; son[now] = 1;
    for( int i = ehead[now]; i; i = e[i].next ) {
        if( e[i].to == fa[now] ) 
            continue;
        fa[ e[i].to ] = now;
        dfs1( e[i].to );
        son[now] += son[ e[i].to ];
        if( son[ mson[now] ] < son[ e[i].to ] ) 
            mson[now] = e[i].to;
    }
    last[now] = cnt;
}

void dfs2( int now ) {
    if( top[now] == 0 ) 
        top[now] = now;
    if( son[now] == 1 ) 
        return ;
    top[ mson[now] ] = top[now];
    dfs2( mson[now] );
    for( int i = ehead[now]; i; i = e[i].next ) {
        if( e[i].to == fa[now] || e[i].to == mson[now] ) 
            continue;
        dfs2( e[i].to );
    }
}

int get_son( int from, int to ) {
    int tmp;
    while( top[from] != top[to] ) {
        tmp = top[from];
        from = fa[ top[from] ];
    }
    if( from == to ) { return tmp; }
    return mson[to];
}
// Heavy-Light Decompostion End

// Seq Start 
struct seq {
    int left, rig, y;
    bool add;
} s[N * 16];
bool cmp( seq a, seq b ) { return a.y < b.y; }

inline void add_seq( int left, int rig, int low, int high ) {
    if( left > rig || low > high ) {
        return ;
    }
    int tmp = 0;
    s[ ++ scnt ] = (seq) { left, rig, low, true };
    s[ ++ scnt ] = (seq) { left, rig, high + 1, false };
    s[ ++ scnt ] = (seq) { low, high, left, true };
    s[ ++ scnt ] = (seq) { low, high, rig + 1, false };
    tmp = Min( rig, high ) - Max( low, left ) + 1;
    if( tmp > 0 ) 
        ans -= 1LL * tmp;
}
// Seq End

int main() {
    freopen( "tree.in", "r", stdin );
    freopen( "tree.out", "w", stdout );
    n = read(), m = read();
    for( int i = 1, x, y; i < n; i++ ) {
        x = read(), y = read();
        add_edge( x, y );
        add_edge( y, x );
    }

    dfs1( 3 );
    dfs2( 3 );

    for( int i = 1, x, y; i <= m; i++ ) {
        x = read(), y = read();
        if( fir[y] < fir[x] ) 
            std::swap( x, y );
        if( fir[x] <= fir[y] && fir[y] <= last[x] ) {
            int tmp = get_son( y, x );
            add_seq( fir[y], last[y], 1, fir[x]);
            add_seq( fir[y], last[y], last[x] + 1, n );
            add_seq( fir[y], last[y], fir[x] + 1, fir[tmp] - 1 );
            add_seq( fir[y], last[y], last[tmp] + 1, last[x] );
        }
        else { 
            add_seq( fir[x], last[x], fir[y], last[y] );
        }
    }

    std::sort( s + 1, s + scnt + 1, cmp );

    int p = 1;
    for( int i = 1; i <= n; i++ ) {
        while( s[p].y <= i && p <= scnt ) {
            if( s[p].left != 0 && s[p].rig != 0 )
                query_add( s[p].left, s[p].rig, s[p].add? 1: -1, 1, 1, n );
            p ++;
        }
        ans = ( ans + 1LL * tree[1] );
    }

    printf( "%I64d\n", ( ( 1LL * n * ( n - 1 ) - ans ) >> 1LL ) );
}

留下评论

电子邮件地址不会被公开。 必填项已用*标注