0w1

CFR 583 D. Once Again... ( DP + Fast Matrix MAXiplication )

Problem - D - Codeforces
I thought this was quite difficult, despite the technique required was not that advanced.
The key is to come up with the dp form:
dp[ i ][ j ][ k ] : maximal length of non-decreasing subsequence of A concatenated k times, such that the first value is greater or equal to A[ i ], and the ending value is A[ j ]
... ( See, it's not that instinctive to imagine of, at least in my view )
With this form, we can easily perform concatenation, which gives us recursive function for dp:
dp[ i ][ j ][ k ] = max{ dp[ i ][ m ][ k - 1 ] + dp[ m ][ j ][ k -1 ] }, for all A[ i ] ≤ A[ m ] ≤ A[ j ]
And since commutative and associative law both apply on MAX, we can put it in form of matrix multiplication to attain optimization, where the identity matrix will be O instead of 1s in the diagonal.
So we will first compute dp[ i ][ j ][ 0 ] for all ( i, j ) with brute force, O( N ^ 3 ). Then, use matrix multiplication, O( N ^ 3 lg T ).
Here's another one pertaining to XOR and AND with similar technique on fast matrix multiplication.

vector< vector< bool > > has_path; // has_path[ i ][ j ] == 1 <=> A[ i ] ≤ A[ j ]

vvi mat_mul( const vvi &a, const vvi &b ){
    vvi res( a.size(), vi( b[ 0 ].size() ) );
    for( int i = 0; i < a.size(); ++i )
        for( int k = 0; k < a[ 0 ].size(); ++k )
            for( int j = 0; j < b[ 0 ].size(); ++j )
                if( has_path[ i ][ k ] and has_path[ k ][ j ] )
                    upmax( res[ i ][ j ], a[ i ][ k ] + b[ k ][ j ] );
    return res;
}

vvi mat_pow( vvi x, int p ){
    vvi res( x.size(), vi( x[ 0 ].size() ) );
    while( p ){
        if( p & 1 )
            res = mat_mul( res, x );
        p >>= 1;
        x = mat_mul( x, x );
    }
    return res;
}

void solve(){
    int N, T; cin >> N >> T;
    vi A( N );
    for( int i = 0; i < N; ++i )
        cin >> A[ i ];

    has_path = vector< vector< bool > >( N, vector< bool >( N ) );
    for( int i = 0; i < N; ++i )
        for( int j = 0; j < N; ++j )
            if( A[ i ] <= A[ j ] )
                has_path[ i ][ j ] = true;

    vvi dp( N, vi( N, 0 ) );
    for( int i = 0; i < N; ++i )
        if( has_path[ i ][ 0 ] )
            dp[ i ][ 0 ] = 1;

    for( int j = 1; j < N; ++j ){
        for( int k = 0; k < N; ++k )
            if( has_path[ k ][ j ] )
                dp[ k ][ j ] = 1;
        for( int k = 0; k < j; ++k )
            if( has_path[ k ][ j ] )
                for( int m = 0; m < N; ++m )
                    if( has_path[ m ][ k ] )
                        upmax( dp[ m ][ j ], dp[ m ][ k ] + 1 );
    }

    dp = mat_pow( dp, T );

    int ans = 0;
    for( int i = 0; i < dp.size(); ++i )
        for( int j = 0; j < dp[ i ].size(); ++j )
            upmax( ans, dp[ i ][ j ] );

    cout << ans << endl;
}