Loading

模拟64—「三元组·简单的字符串·环路·过河」

三元组

比较简单的扫描线,枚举 \(c\) 的位置,用树状数组维护一下 \(a,b\) 的数对有多少对。
因为可以取等所以先加入在统计答案,考场define int long long ,被卡常了。

简单的字符串

\(\rm nb\) 题,让我入门了 \(\rm border\) 理论。
但发现自己有个结论不会证明,所以挖个坑。
放个链接,有时间要仔细读一下 link

填坑 upd 9.29(因为颓爆了(以后颓爆果断直接补博客))

首先让可以把循环同构串 \(A,B\) 拆分一下,肯定存在一种拆分方式使得 \(A=uv,B=vu\)

现在我来证明一个结论:

\(u\)\(AB\) 的长度小于等于一半的最长 \(\rm border\) ”或者“\(v\)\(BA\) 的长度小于等于一半的最长 \(\rm border\) 至少有一个为真 ”

其实等价于证明 \(u\) 不是 \(AB\) 的长度小于等于一半的最长 \(\rm border\) 的时候,\(v\) 一定是 \(BA\) 的长度小于等于一半的最长 \(\rm border\)

考虑\(u\) 不是 \(AB\) 的长度小于等于一半的最长 \(\rm border\)什么时候会发生。

当且仅当存在一个串 \(T\) , 使得 \(T\)\(B\)\(\rm border\) 并且 \(A+T=T+A\)

分类讨论 \(A\leq T\)\(A>T\) ,发现一个可以表示成另一个的整周期,那么此时如果第二个也不满足,那么一定能找到一种重新的划分方式
(不想展开了,可以自己手模理解。)

所以第一种情况可以跳等差数列直接找到小于等于一半的(最多3次(两次找公差,一次跳到小于))。
第二种情况直接 \(hash\) 就可以。

环路

首先可以写出来一个简单的暴力。
设邻接矩阵为 \(A\) ,则 \(A^k[i][j]\) 的实际意义 \(i\) 通过恰好 \(k\) 步走到 \(j\) 的方案数(本质是 \(dp\) 的矩阵优化)。

那么找环就是看找能走到自己的方案数,也就是矩阵的主对角线上面数字的和。
也就是求出矩阵的连续幂次和,最后把主对角线上面的数字加起来。

可以发现可以用分治来做,把他分成 \(1-mid\)\(mid+1 - r\) 两个部分,可以把 \(A^{mid}\) 提出来,然后问题变成了
\(A^{mid}*(1+solve(r>>1))\)

如果每次都快速幂一次,复杂度是 \(log^2\) 的,发现我们只需要计算一条快速幂链上面的值。
所以在分治的时候模拟快速幂就行了。

放一个 \(n^3logn\) 复杂度的代码(一共 \(\rm 800^+ ms\) )。

#include <bits/stdc++.h>
#define int long long
#define R register int 
#define scanf Ruusupuu = scanf
#define freopen rsp_5u = freopen
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)

int Ruusupuu ;
FILE * rsp_5u ;

using namespace std ;
typedef long long L ;
typedef double D ;
const int N = 1e2 + 2 ;

inline int read(){
    int w = 0 ; bool fg = 0 ; char ch = getchar() ;
    while( ch < '0' || ch > '9' ) ch = getchar() ;
    while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ 48 ) , ch = getchar() ;
    return fg ? -w : w ;
}

int n , K , P , ans , mat [N][N] , mmat [N][N] , po [N][N] , zc [N][N] ;
char s [N] ;

inline int A( int a , int b ){ return ( a + b ) >= P ? ( a + b - P ) : ( a + b ) ; }
inline int T( int a , int b ){ return ( 1ll * a * b ) - ( ( 1ll * a * b ) / P ) * P ; }

void sc(){
    n = read() ;
    for( R i = 1 ; i <= n ; i ++ ){
        scanf( "%s" , s + 1 ) ;
        for( R j = 1 ; j <= n ; j ++ ) mmat [i][j] = mat [i][j] = ( s [j] == 'Y' ) ;
    } K = read() , P = read() ;
}

int b [N][N] , pp [N][N] ;

inline void mul( int a [N][N] , int c [N][N] ){
    memset( b , 0 , sizeof( b ) ) ;
    for( R k = 1 ; k <= n ; k ++ )for( R i = 1 ; i <= n ; i ++ ) {
        if( !a [i][k] ) continue ;
        for( R j = 1 ; j <= n ; j ++ )
            b [i][j] = ( b [i][j] + ( a [i][k] * c [k][j] ) ) % P ;
    } memcpy( a , b , sizeof( b ) ) ;
}

inline void mulself( int a [N][N] ){
    memset( b , 0 , sizeof( b ) ) ;
    for( R k = 1 ; k <= n ; k ++ )for( R i = 1 ; i <= n ; i ++ ) {
        if( !a [i][k] ) continue ;
        for( R j = 1 ; j <= n ; j ++ )
            b [i][j] = ( b [i][j] + ( a [i][k] * a [k][j] ) ) % P ;
    } memcpy( a , b , sizeof( b ) ) ;
}

inline void add( int a [N][N] , int b [N][N] ){ for( R i = 1 ; i <= n ; i ++ ) for( R j = 1 ; j <= n ; j ++ ) a [i][j] = A( a [i][j] , b [i][j] ) ; }

int lst [N][N] ;

void slv( int lef ){ // for( R i = 1 ; i <= lef ; i ++ ) ans += pow( mat , i ) ;
    if( lef == 1 ) return memcpy( lst , mmat , sizeof( lst ) ) , void() ;
    if( lef & 1 ){
        slv( lef >> 1 ) ;
        memcpy( zc , mat , sizeof( mat ) ) , mul( mat , lst ) , add( mat , zc ) ;
        mulself( lst ) , mul( lst , mmat ) ;
        add( mat , lst ) ;
    } else{
        slv( ( lef >> 1 ) ) ;
        memcpy( zc , mat , sizeof( mat ) ) , mul( mat , lst ) , add( mat , zc ) ;
        mulself( lst ) ;
    }
}

void work(){    
    int tim = K - 1 ;
    slv( tim ) ;
    for( R i = 1 ; i <= n ; i ++ ) ans = A( ans , mat [i][i] ) ;
    printf( "%lld\n" , ans ) ;
}

signed main(){  fre(tour);
    sc() ;
    work() ;
    return 0 ;
}

过河

稍后

总结

  1. T1被卡常了,闲着没事还是多取模关掉 define int long long 不要偷懒了。
  2. T2数据没捆绑,没啥好说的。
  3. T3考试最后一分钟打完的,统计答案的时候没取模爆0了,OI宝典还是要熟记,打的时候就要取模。
posted @ 2021-09-29 19:44  Soresen  阅读(67)  评论(1编辑  收藏  举报