jzyzoj題目選作

P1815

一道貪心,喵?c++

好吧其實就是個貪心,最大值比較簡單,最小值不太好搞看看代碼本身理解git

#include <bits/stdc++.h>
#define LL long long
using namespace std;


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

int main()
{
    LL n , x , y , k , xx , yy , w;
    LL ans ;
    while( scanf( "%lld" , & x ) == 1 )
    {
        xx = x , y = yy = read() , n = read();
        //特判
        if( n == 1 )
        {
            if( x > y ) puts("3 3");
            else if( x == y ) puts("1 1");
            else puts("0 0");
            continue;
        }
        //max
        w = n - 1;
        k = min( n - 1 , x ) , x -= k , w -= k , ans = k * 3 ;
        if( x > y ) ans += 3;
        else if( x == y ) ans += 1;
        ans += w;
        printf("%lld " , ans );
        //min
        if( xx > yy )
        {

            n -- , ans = 3 ; //把全部的 x 都放到一個格子裏,保證只贏一次
            k = min( yy , n ) , ans += n - k; //再給剩下的格子儘量的放 y
            printf( "%lld\n" , ans );
            continue;
        }

        if( xx == yy )
        {
            if( n >= 3 && yy >= 3 )
            {
                // 同 xx > yy
                n -- , ans = 3 ;
                k = min( yy , n ) , ans += n - k;
                printf( "%lld\n" , ans );
            }
            else printf( "%lld\n" , n ); //直接所有平局
            continue;
        }

        // 對於xx<yy的狀況
        n -- ;
        ans = 3 + n - ( min( n , yy ) ); // 把全部的 x 放在一個格子裏,剩下的格子每一個放一個 y
        yy -= xx;
        ans = min( ans , 1 + n - min( n , yy ) ); // 在一個格子裏抵消掉全部的 x 後面的所有放 y
        yy --;
        ans = min( ans , n - min( n , yy ) ); // 在保證不會贏的狀況下,儘量的放 y
        printf( "%lld\n" , ans );
    }
    return 0;
}

P1810

這就是道數據結構模板題,方法不少我用是分塊算法

#include<bits/stdc++.h>
#define L( x ) ( ( x - 1 ) * len + 1 )
#define R( x ) ( x * len )
#define pos( x ) ( ( x - 1 ) / len + 1 )
using namespace std;


const int N = 100000 , M = 1000;
int n , m , len , pos[N] , last , gp[M];
int tag[M] , v[N];


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


int main()
{
    n = read() , last = m = read() , len = sqrt( 1.0 * m );
    for( register int i = 1 ; i <= m ; gp[ pos(i) ] ++ , v[i] = 1 , i ++ );
    for( register int l , r , p , q , sum ; n >= 1 ; n -- )
    {
        l = read() , r = read() , p = pos( l ) , q = pos( r ) , sum = 0;
        if( last == 0 )
        {
            puts("0");
            continue;
        }
        if( p == q )
        {
            for( register int i = l ; i <= r ; i ++ )
            {
                if( tag[p] ) break;
                sum += v[i] , gp[q] -= v[i] , v[i] = 0;
            }
            printf( "%d\n" , last - sum );
            last -= sum ;
            continue;
        }
        for( register int i = l ; i <= R( p ) && !tag[p] ; sum += v[i] , gp[p] -= v[i] , v[i] = 0 , i ++ );
        for( register int i = r ; i >= L( q ) ; i -- )
        {
            if( tag[q] ) break;
            sum += v[i] , gp[q] -= v[i] , v[i] = 0;
        }
        for( register int i = p + 1 ; i <= q - 1 ; i ++ )
        {
            if( tag[i] ) continue;
            sum += gp[i] , tag[i] = 1;
        }
        printf( "%d\n" , last - sum );
        last -= sum;
    }
}

P1993

顯然是道樹狀數組的題目,區間修改,單點查詢,維護一個差分序列數組

本題主要有兩個坑點數據結構

  1. 下標從\(0\)開始,樹狀數組不能維護從\(0\)開始的下標,因此要所有加一
  2. 卡輸入輸出,喵呀
#include <bits/stdc++.h>
#define lowbit( x ) ( x & -x )
using namespace std;


const int N = 1000005;
int n , m , bit[N];
char buf[ 1 << 15 ] , * fs , * ft ;


inline char getc(){ return ( fs == ft && ( ft = ( fs = buf ) + fread( buf , 1 , 1 << 15 , stdin ) , fs == ft ) ) ? 0 : * fs ++ ; }

inline int read()
{
    register int x = 0 , f = 1 ;
    register char ch = getc() ;
    while( !isdigit( ch ) )
    {
        if( ch == '-' )  f = -1 ;
        ch = getc();
    }
    while( isdigit( ch ))
    {
        x = ( x << 3 ) + ( x << 1 ) + ch - '0' ;
        ch = getc();
    }
    return x * f;
}

inline void put( int x )
{
    if( x == 0 )
    {
        putchar( '0' );
        putchar( '\n' );
        return;
    }
    if( x < 0 )
    {
        putchar( '-' );
        x =- x;
    }
    register int num = 0;
    register char ch[16];
    while( x ) ch[ ++ num ] = x % 10 + '0' , x /= 10 ;
    while( num ) putchar(ch[num--]) ;
    putchar('\n');
}

inline int query( int x )
{
    register int sum = 0;
    for( register int i = x ; i >= 1 ; i -= lowbit( i ) ) sum += bit[i];
    return sum;
}

inline void add( int x , int w )
{
    for( register int i = x ; i <= n ; i += lowbit( i ) ) bit[i] += w;
    return ;
}


int main()
{
    n = read() + 1, m = read();
    for( register int op , l , r ; m >= 1 ; m -- )
    {
        op = read() , l = read() + 1 ;
        if( op == 1 ) r = read() + 1 , add( l , 1 ) , add( r + 1 , -1 );
        else put( query(l) );
    }
    return 0;
}

P1813

用線段樹統計一下個數,最後作個除法,沒啥難的,主要是要知道怎麼用線段樹維護一個桶吧優化

#include <bits/stdc++.h>
#define LL long long
#define lowbit( x ) ( x & - x )
using namespace std;


const int N = 1000010;
int n , m , a[N] , bit[N] , tp;
LL sum;

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

inline void add( int x )
{
    for( register int i = x ; i <= tp ; bit[i] ++ , i += lowbit( i ) );
}

inline LL query( int x )
{
    register LL res = 0;
    for( register int i = x ; i >= 1 ; res += bit[i] , i -= lowbit( i ) );
    return res;
}


int main()
{
    n = read() , m = read();
    for( register int i = 1 ; i <= n ; a[i] = read() , tp = max( tp , a[i] ) , i ++ );
    for( register int i = 1 ; i <= n ; add( a[i] ) , i ++ );
    for( register int i = 1 ; i <= n ; i ++ )
    {
        sum += query( m - a[i] );
        if( a[i] <= m - a[i] ) sum --;
    }
    register double ans = (double)sum / (double)( n * n - n );
    printf( "%.2lf\n" , ans );
    return 0;
}

P1280

都說這是到動態規劃,當我不想用動態規劃,因而就有了搜索的作法ui

其實找一份揹包的代碼,你會發現原理都差很少spa

y > m時,ans = max( ans , cnt - 1 )的緣由時此時使用了y + 1CD因此要減去一個,其餘真沒啥難度了code

#include <bits/stdc++.h>
using namespace std;

const int N = 25;
int n , m , t , w[N] , ans;


inline void dfs( int x , int y , int nt ,  int cnt )
{
    if( t < w[x] )
    {
        dfs( x + 1 , y , nt , cnt );
        return ;
    }
    if( y > m  )
    {
        ans = max( cnt - 1 , ans );
        return ;
    }
    if( x > n )
    {
        ans = max( cnt , ans);
        return ;
    }
    ans = max( cnt , ans );
    if( w[x] + nt <= t ) dfs( x + 1 , y , nt + w[x] , cnt + 1 );
    else dfs( x + 1 , y + 1 , w[x] , cnt + 1 );
    dfs( x + 1 , y , nt , cnt );
    return ;
}

int main()
{
    cin >> n >> t >> m ;
    for( register int i = 1 ; i <= n ; i ++ ) cin >> w[i];
    dfs( 1 , 1 , 0 , 0 );
    cout << ans << endl;
    return 0;
}

P1262

線性\(dp\),lis問題的四種狀況,考碼力和細節吧ip

#include <bits/stdc++.h>
using namespace std;

const int N = 7e5 + 5;
int n , a[N] , q[N] , tot ;


inline int read()
{
    register int x = 0 , f = 1;
    register char ch = getchar();
    while( ch < '0' || ch > '9' )
    {
        if( ch == '-' ) f = -1;
        ch = getchar();
    }
    while( ch >= '0' && ch <= '9' )
    {
        x = ( x << 3 ) + ( x << 1 ) + ch - '0';
        ch = getchar();
    }
    return x * f;
}


int main()
{
    n = read();
    for( register int i = 1 ; i <= n ; a[i] = read() , i ++ );

    // 最長上升序列長度
    tot = 1 , q[1] = a[1];
    for( register int i = 2 ; i <= n ; i ++ )
    {
        if( a[i] > q[ tot ] ) q[ ++ tot ] = a[i];
        else *lower_bound( q + 1 , q + 1 + tot , a[i] ) = a[i];
    }
    cout << tot << endl;

    // 最長降低序列長度
    tot = 1 , q[1] = a[1];
    for( register int i = 2 ; i <= n ; i ++ )
    {
        if( a[i] < q[ tot ] ) q[ ++ tot ] = a[i];
        else *lower_bound( q + 1 , q + 1 + tot , a[i] , greater<int>() ) = a[i];
    }
    cout << tot << endl;

    // 最長不上升序列長度
    tot = 1 , q[1] = a[1];
    for( register int i = 2 ; i <= n ; i ++ )
    {
        if( a[i] <= q[ tot ] ) q[ ++ tot ] = a[i];
        else *upper_bound( q + 1 , q + 1 + tot , a[i] , greater<int>() ) = a[i];
    }
    cout << tot << endl;

    // 最長不降低序列長度
    tot = 1 , q[1] = a[1];
    for( register int i = 2 ; i <= n ; i ++ )
    {
        if( a[i] >= q[ tot ] ) q[ ++ tot ] = a[i];
        else *upper_bound( q + 1 , q + 1 + tot , a[i] ) = a[i];
    }
    cout << tot << endl;

}

P1448

由於是\(noip\)的原題,因此在哪都有,但咱們校\(OJ\)卡常數

因此就要換一種作法

咱們已知秦九韶能夠\(O(n)\)的計算出每一個數,那麼是否能夠優化這個複雜度呢
\[ 設原式爲f(x),b=x\mod p \\ \therefore f(x)\equiv f(b) (\mod p) \\ \because f(x) \equiv 0 (\mod p) , \therefore f(b)\equiv 0(\mod p) \]
因此咱們能夠先預處理出\(1\dots p-1\)中全部的數,而後對於\(\forall i\in [1,m] f(i)\)咱們判斷\(f(i\mod p)\)便可

這樣咱們只須要縮小\(p\)就能夠下降複雜度,可是若是\(p\)比較小就可能會出現衝突,因此咱們取多個模數便可

根據實測在\(10^5\)\(4\)個模數是最優的

#include <bits/stdc++.h>
using namespace std;


const int N = 105 , M = 1e6 + 5 , p1 = 11261 , p2 = 19997 , p3 = 14843 , p4 = 22877;
int n , m , a[N] , b[N] , c[N] , d[N] , tot , ans[M];
bool f1[ p1 + 10 ] , f2[ p2 + 10 ] , f3[ p3 + 10 ] , f4[ p4 + 10 ];


inline void read( int & a , int & b , int & c , int & d )
{
    register int f = 1;
    register char ch = getchar();
    for( ; ch < '0' || ch > '9' ; ( ch == '-' ? f = - 1 : f ) , ch = getchar() );
    for( ; ch >= '0' && ch <= '9' ; a = ( ( a << 3 ) % p1 + ( a << 1 ) % p1 + ch - '0' ) % p1 , b = ( ( b << 3 ) % p2 + ( b << 1 ) % p2 + ch - '0' ) % p2  , c = ( ( c << 3 ) % p3 + ( c << 1 ) % p3 + ch - '0' ) % p3 , d = ( ( d << 3 ) % p4 + ( d << 1 ) % p4 + ch - '0' ) % p4 , ch = getchar() );
    a *= f , b *= f , c *= f , d *= f ;
    return ;
}

inline bool check( int x , int p , int *a )
{
    int s = a[n];
    for( register int i = n - 1 ; i >= 0 ; i -- ) s = ( s * x + a[i] ) % p ;
    return !s;
}

inline void work( int p , bool * f , int * a )
{
    f[0] = !a[0];
    for( register int i = 1 ; i < p ; i ++ ) f[i] = check( i , p , a );
    return ;
}


int main()
{
    cin >> n >> m;
    for( register int i = 0 ; i <= n ; read( a[i] , b[i] , c[i] , d[i] ) , i ++ );
    
    work( p1 , f1 , a ) , work( p2 , f2 , b ) , work( p3 , f3 , c ) , work( p4 , f4 , d );
    
    for( register int i = 1 ; i <= m ; i ++ )
    {
        if( f1[ i % p1 ] && f2[ i % p2 ] && f3[ i % p3 ] && f4[ i % p4 ] ) ans[ ++ tot ] = i;
    }
    
    printf( "%d\n" , tot );
    for( register int i = 1 ; i <= tot ; printf( "%d\n" , ans[i] ) , i ++ );
    return 0;
}

P1447

\(Noip\)原題,首先考慮如何知足第一個條件,反向建圖,充目標點開始作\(DFS\),把全部到達的點打上標記,這樣若是一個點的子節點所有都被打上標記,那麼這個點就知足第一條件

第二個條件就是裸的最短路,直接在被標記的點的中跑一遍最短路

#include <bits/stdc++.h>
#define PII pair< int, int >
#define F first
#define S second
#define vit vector<int>::iterator 
using namespace std;


const int N = 10005 , INF = 0x7f7f7f7f;
int n , m , st , ed , dis[N];
bool vis[N] , can[N];
set< PII > s;
vector<int> e[N] , t[N] ;


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

inline void add( int x , int y ) { e[x].push_back(y) , t[y].push_back(x); }

inline void dfs( int x )
{
    can[x] = 1;
    for( register vit v = t[x].begin() ; v != t[x].end() ; v ++ )
    {
        if( can[*v] ) continue; 
        dfs( *v );
    }
    return ;
}

inline bool panding( int x )
{
    for( register vit v = e[x].begin() ; v != e[x].end() ; v ++ )
    {
        if( can[*v] ) continue;
        return 1;
    }
    return 0;
}

inline void Dijkstra()
{
    for( register int i = 1 ; i <= n ; i ++ ) dis[i] = INF;
    s.insert( { 0 , st } ) , dis[st] = 0;
    
    for( register int u , w; s.size() ; )
    {
        u = s.begin() -> S , s.erase( s.begin() );
        if( vis[u] ) continue;
        vis[u] = 1;
        if( panding(u) ) continue;
        if( u == ed ) return ;
        for( register vit v = e[u].begin() ; v != e[u].end() ; v ++ )
        {
            if( dis[*v] <= dis[u] + 1 ) continue;
            dis[*v] = dis[u] + 1;
            s.insert( { dis[*v] , *v } );
        }
    }
}


int main()
{
    n = read() , m = read();
    for( register int x , y ; m >= 1 ; x = read() , y = read() , add( x , y ) , m -- );
    st = read() , ed = read();
    
    dfs(ed);
    
    Dijkstra();
    
    cout << ( dis[ed] == INF ? -1 : dis[ed] ) << endl;
    return 0;
}

P1846

一道搜索題,能夠採用一個非正確的算法水過去

咱們判斷程序運行的時間,若是接進\(T\)的時候輸出當前的最優解,而後直接結束程序

這個代碼不能保證正確性,也不能保證複雜度,正確性取決於測評機是速度

但要知道在比賽中,若是數據達到必定規模,你沒法繼續優化複雜度的狀況下,經過這種方式來偏分,對一個就是賺一個

這種方式確定比你直接輸出隨機數正確性高

#include <bits/stdc++.h>
#define exmax( x , y , z ) ( max( x , max( y , z ) ) ) 
using namespace std;


const int N = 33 , INF = 0x7f7f7f7f , ed = 980;
int n , a[N] , ans = INF , t1 , t2 , sum;


inline void dfs( int t , int s1 , int s2 , int s3 )
{
    if( exmax( s1 , s2 , s3 ) >= ans ) return ;
    if( t > n )
    {
        ans = min( ans , exmax( s1 , s2 , s3 ) );
        return ;
    }
    t2 = clock();
    if( t2 - t1 >= ed )
    {
        cout << ans << endl;
        exit(0);
    }
    dfs( t + 1 , s1 + a[t] , s2 , s3 );
    dfs( t + 1 , s1 , s2 + a[t] , s3 );
    dfs( t + 1 , s1 , s2 , s3 + a[t] );
    return ;
}

int main()
{
    t1 = clock();
    cin >> n;
    for( register int i = 1 ; i <= n ; i ++ ) cin >> a[i];
    sort( a + 1 , a + 1 + n , greater<int>() );
    dfs( 1 , 0 , 0 , 0 );
    cout << ans << endl;
    return 0;
}
相關文章
相關標籤/搜索