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;
}
相关文章
相关标签/搜索