一道貪心,喵?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; }
這就是道數據結構模板題,方法不少我用是分塊算法
#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; } }
顯然是道樹狀數組的題目,區間修改,單點查詢,維護一個差分序列數組
本題主要有兩個坑點數據結構
#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; }
用線段樹統計一下個數,最後作個除法,沒啥難的,主要是要知道怎麼用線段樹維護一個桶吧優化
#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; }
都說這是到動態規劃,當我不想用動態規劃,因而就有了搜索的作法ui
其實找一份揹包的代碼,你會發現原理都差很少spa
y > m
時,ans = max( ans , cnt - 1 )
的緣由時此時使用了y + 1
個CD
因此要減去一個,其餘真沒啥難度了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; }
線性\(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; }
由於是\(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; }
\(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; }
一道搜索題,能夠採用一個非正確的算法水過去
咱們判斷程序運行的時間,若是接進\(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; }