1. 程式人生 > >牛客寒假算法基礎集訓營6 解題報告

牛客寒假算法基礎集訓營6 解題報告

全部 是我 抽象 bits 了解 n) algo build spfa

前言

離ak最近的1場qwq

寫了9題,再給我5min就能ak的

混了個rk20多qwq

怎麽天天出原題啊我做過的都有3道了

A

做法 : 小學奧數

小學奧數題吧...
只要你智商在線,人腦裏模擬一下不就行了
顯然也就那麽幾種情況。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 10000100

ll n, m;

int main() {
    scanf("%lld%lld", &n, &m);
    if(n > m * 9 || n < m * 6) return puts("jgzjgzjgz"), 0;
    ll c = n - m * 6;
    if(c > m) puts("0");
    else printf("%lld\n", m - c);
    return 0;
}

B

做法:暴力/二分

一眼秒是二分。
具體做法是二分天數,然後等差數列求和判斷。
但是寫掛了不知道是溢出了還是什麽。賽後交了是90分。
最後一個大爺告訴我暴力能過...
怪不得是全場通過最多

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 5000050
  
ll n, m, d, x;
  
bool check(ll x) {
    __int128 s = (__int128)(x * d + n + n) * x / 2;
    return s >= (__int128)m;
}
  
int main() {
    scanf("%lld%lld%lld%lld", &n, &m, &d, &x);
    ll ans = 0, res = 0;
    while(res < m) {
        res += n;
        n += d;
        ans++;
    }
    printf("%lld\n", ans);
}

C

做法:貪心

因為沒啥特殊性質,顯然直接按塗上去的價值排序就好,然後取前n大的價值即可
是個sb題

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 10000100
int n, m;
struct Node {
    int a, b;
} a[N];
 
bool operator < (Node a, Node b) {
    return a.b > b.b;
}
 
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; ++i) scanf("%d", &a[i].a);  for(int i = 1; i <= m; ++i) scanf("%d", &a[i].b);
    sort(a + 1, a + m + 1);
    ll ans = 0;
    for(int i = 1; i <= m; ++i) {
        if(n >= a[i].a) {
            ans += 1ll * a[i].a * a[i].b;
            n -= a[i].a;
        } else {
//            if(!n) break;
            ans += 1ll * a[i].b * n;
            break;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

D

做法:貪心

之前做過類似的題,貌似是洛谷月賽。
絕對值小於等於1,很麻煩,所以不如欽定每一堆只要能拿就一定拿完,如果有一個要湊的就從右邊拿。
這個正確性挺顯然的吧,如果不是很理解可以把樣例畫出來自己看看

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 5000050
 
int n;
ll a[N];

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
    ll ans = 0;
    for(int i = 1; i <= n; ++i) {
        ans += a[i] / 2;
        if(a[i] % 2 == 0) continue;
        if(a[i + 1] == 0) continue;
        ans++; a[i + 1]--;
    }
    printf("%lld\n", ans);
}

E

做法:二維前綴和

sb題啊。。。
一開始以為是個神仙題。我看錯了題意以為d是每次給定的,結果是先給的。。。
那麽就和NOIP2016組合數問題基本一樣了
怎麽天天出原題啊
預處理\(sum[i][j]\)表示前\([i,j]\)個格子,滿足值大於等於d的有多少個。
那麽按二維前綴和的思路容斥一下就好。如果不了解二維前綴和的看一下下面這個式子也就懂了
\(sum[l2][r2] + sum[l1 - 1][r1 - 1] - sum[l1 - 1][r2] - sum[l2][r1 - 1]\)
實在不行再畫個圖?那圖網上找找就好,爛大街的圖
至於\(n*m<1e6\)開個vector就好
據說卡空間,不過我是沒給卡到

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <deque>
#include <map>
#include <set>

#define ll long long
#define inf 0x3f3f3f3f
#define il inline

namespace io {

    #define in(a) a=read()
    #define out(a) write(a)
    #define outn(a) out(a),putchar('\n')

    #define I_int ll
    inline I_int read() {
        I_int x = 0 , f = 1 ; char c = getchar() ;
        while( c < '0' || c > '9' ) { if( c == '-' ) f = -1 ; c = getchar() ; }
        while( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar() ; }
        return x * f ;
    }
    char F[ 200 ] ;
    inline void write( I_int x ) {
        if( x == 0 ) { putchar( '0' ) ; return ; }
        I_int tmp = x > 0 ? x : -x ;
        if( x < 0 ) putchar( '-' ) ;
        int cnt = 0 ;
        while( tmp > 0 ) {
            F[ cnt ++ ] = tmp % 10 + '0' ;
            tmp /= 10 ;
        }
        while( cnt > 0 ) putchar( F[ -- cnt ] ) ;
    }
    #undef I_int

}
using namespace io ;

using namespace std ;

#define N 1000100

int n, m, d;
vector<int>sum[N];

int main() {
    n = read(), m = read(), d = read();
    for(int i = 1; i <= n; ++i) sum[i].push_back(0); for(int i = 0; i <= m; ++i) sum[0].push_back(0);
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= m; ++j) {
            int x = read();
            x < d ? sum[i].push_back(0) : sum[i].push_back(1);
        }
    } 
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= m; ++j) {
            sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
        }
    }
    int Q = read();
    while(Q--) {
        int l1 = read(), r1 = read(), l2 = read(), r2 = read();
        int ans = sum[l2][r2] + sum[l1 - 1][r1 - 1] - sum[l1 - 1][r2] - sum[l2][r1 - 1];
        printf("%d\n", ans);
    }
    return 0;
}

F

做法:搜索+構造

唯一的難題。
大致做法是寬搜一下,因為其實合法的構造方案很少。
結束了就棄了沒寫,這題具體做法還是看看官方題解吧。。。

G

做法:按位貪心

挺顯然的一道題,3min秒了,然後因為上界搞錯調了要1h。
或的規則是有1則1,那麽考慮每一位,如果\(b-a\)(即a到b變換了的位數,這中間肯定有1)比這一位大,那麽顯然就會有一個1跟他或一下,那麽這一位就固定為1了
上界要枚舉到62...我上界試了31,32,63都掛了...最後62就過了,有毒

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 5000050
  
ll a, b;

int main() {
    while(~scanf("%lld%lld", &a, &b)) {
        ll ans = b | a;
        for(ll k = 63; k >= 0; k--) {
            if((ll)(1ll << (k)) <= (b - a)) ans |= (1ll << (k));
        }
        printf("%lld\n", ans);
    }
}
/*
枚舉每一位
對於一位k,如果(a-b)>=(1<<k),那麽這一位就可以|1
*/

H

做法:暴力枚舉+線段樹優化

這題做了好久...做完這題,F時間就不夠了...
一開始沒啥思路,然後蔡隊在牛客群上說了:“H有啥難的,暴力枚舉啊”
orz。
然後往這方面想了想就想出來了。
顯然比較麻煩的就是這個全體右移k位。
如果我們知道了一共要右移x次,那麽其實每頭豬的最小代價其實是確定了的,就是\(min(a_i,min(a_{i-x}...a_{i-1})+x)\)(這是因為我們可以安排放入這頭豬的時間,比如我們要取\(i-2\)這個地方的代價來填\(i\)這個位置,那麽我們在剩下兩次右移次數的時候放入\(i-2\)這個地方就好了。如果要用原來的價值,那麽全部右移完後再放進去就好)
所以現在就明朗了。
枚舉右移次數k,對於每個k算出最小代價,使用數據結構求區間最小值就可以了。
我一開始st表寫掛了。。。爆了4發。最後用了線段樹過了。
至於n右移了會到1的,可以分類討論一下,也可以直接斷環成鏈。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100010
const ll inf = 1e18;
#define int long long

int n, x;
int a[N];

struct tree{
    int l,r,mn;
}t[N<<2];
#define mid ((l + r) >> 1)
#define lc (rt << 1)
#define rc (rt << 1 | 1)
void pushup(int rt) {
    t[rt].mn = min(t[lc].mn, t[rc].mn);
}
void build(int l, int r, int rt) {
    t[rt].l = l; t[rt].r = r;
    if(l == r) {
        t[rt].mn = a[l];
        return;
    }
    build(l, mid, lc); build(mid + 1, r, rc); pushup(rt);
}
#define l t[rt].l
#define r t[rt].r
int query(int L, int R, int rt) {
    if(L <= l && r <= R) {
        return t[rt].mn;
    }
    int ans = inf;
    if(L <= mid) ans = min(ans, query(L, R, lc));
    if(R > mid) ans = min(ans, query(L, R, rc));
    return ans;
}
#undef l
#undef r
#undef mid
#undef lc
#undef rc

signed main() {
    scanf("%lld%lld", &n, &x);
    for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
    build(1, n, 1);
    ll ans = 1e18;
    for(int k = 0; k <= n; ++k) {
        ll sum = k * x;
        for(int i = 1; i <= n; ++i) {
            if(i - k < 1) sum += min(query(1, i, 1), query(n - k + i, n, 1));
            else sum += query(i - k, i, 1);
        }
        ans = min(ans, sum);
    }
    printf("%lld\n", ans);
    return 0;
}

I

棧(括號序列)

本質上就是個括號序列
因為註意到每次最多只能+10分,最少+5分(+0我們肯定不會去選他)。
而且每個得分需要的次數是一樣的。於是我們有一個貪心的做法。每次把當前數和棧頂比較,如果可以匹配則彈出並將答案+10,如果不行就扔進棧裏。
最後把棧裏的兩兩彈出並+5即可
抽象一下,就是個括號序列匹配而已

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 1000010
  
char s[N];
int st[N];

int main() {
    scanf("%s", s + 1);
    int ans = 0, n = strlen(s + 1), top = 0;
    for(int i = 1; i <= n; ++i) {
        if(!top) {
            st[++top] = s[i] - '0';
            continue;
        }
        if(s[i] - '0' == st[top]) {
            top--;
            ans += 10;
        } else {
            st[++top] = s[i] - '0';
        }
    }
    while(top) {
        top -= 2;
        ans += 5;
    }
    printf("%d\n", ans);
}

J

做法:搜索

直接搜就行了。。。
因為是原題所以我就懶得寫了,我拿了我以前寫過的那題的代碼交了,用的spfa。
原題是CF1064D
http://codeforces.com/contest/1064/problem/D
正好我當時打過。。。

#include <cstdio>
#include <cstring>
#include <algorithm>

#define ll long long
#define debug printf("233\n")
#define inf 0x3f3f3f3f 
#define il inline 
#define in1(a) read(a)
#define in2(a,b) in1(a),in1(b)
#define in3(a,b,c) in2(a,b),in1(c)
#define in4(a,b,c,d) in2(a,b),in2(c,d)

inline void read( int &x ){
    x = 0 ; int f = 1 ; char c = getchar() ;
    while( c < '0' || c > '9' ) {
        if( c == '-' ) f = -1 ;
        c = getchar() ;
    }
    while( c >= '0' && c <= '9' ) {
        x = (x << 1) + (x << 3) + c - 48 ;
        c = getchar() ;
    }
    x *= f ;
}

inline void readl( ll &x ){
    x = 0 ; ll f = 1 ; char c = getchar() ;
    while( c < '0' || c > '9' ) {
        if( c == '-' ) f = -1 ;
        c = getchar() ;
    }
    while( c >= '0' && c <= '9' ) {
        x = (x << 1) + (x << 3) + c - 48 ;
        c = getchar() ;
    }
    x *= f ;
}

using namespace std ;

#define N 2010

int n , m , r , c , x , y ;
char ch[ N ][ N ] ;
struct edge {
    int to , nxt , v ;
} e[ N * N * 4 ] , E[ N * N * 4 ];
int head[ N * N ] , cnt , Head[ N * N ] , Cnt ;
int vis[ N * N ] , d[ N * N ] , Vis[ N * N ] , D[ N * N ] ;
int q[ 1000100 ] ;

void ins1( int u , int v , int w ) {
    e[ ++ cnt ].to = v ;
    e[ cnt ].nxt = head[ u ] ;
    e[ cnt ].v = w ;
    head[ u ] = cnt ;
}

void ins2( int u , int v , int w ) {
    E[ ++ Cnt ].to = v ;
    E[ Cnt ].nxt = Head[ u ] ;
    E[ Cnt ].v = w ;
    Head[ u ] = Cnt ;
}

void spfa() {
    int s = (r-1) * m + c ;
    q[ 1 ] = s ;
    int l = 1 , r = 2 ;
    for( int i = 1 ; i <= n * m ; i ++ ) d[ i ] = inf ;
    d[ s ] = 0 ;
    vis[ s ] = 1 ;
    while( l != r ) {
        int u = q[ l ++ ] ;
        vis[ u ] = 0 ;
        if( l == 1000000 ) l = 1 ;
        for( int i = head[ u ] ; i ;  i = e[ i ].nxt ) {
            int v = e[ i ].to ;
            if( d[ v ] > d[ u ] + e[ i ].v ) {
                d[ v ] = d[ u ] + e[ i ].v ;
                if( !vis[ v ] ) {
                    vis[ v ] = 1 ; 
                    q[ r ++ ] = v ;
                    if( r == 1000000 ) r = 1 ;
                }
            }
        }
    }
}

void spfa2() {
    int s = (r-1) * m + c ;
    int l = 1 , r = 2 ;
    q[ 1 ] = s ;Vis[ s ] = 1 ;
    for( int i = 1 ; i <= n * m ; i ++ ) D[ i ] = inf ;
    D[ s ] = 0 ;
    while( l != r ) {
        int u = q[ l ++ ] ;
        Vis[ u ] = 0 ;
        if( l == 1000000 ) l = 1 ;
        for( int i = Head[ u ] ; i ;  i = E[ i ].nxt ) {
            int v = E[ i ].to ;
            if( D[ v ] > D[ u ] + E[ i ].v ) {
                D[ v ] = D[ u ] + E[ i ].v ;
                if( !Vis[ v ] ) {
                    Vis[ v ] = 1 ; 
                    q[ r ++ ] = v ;
                    if( r == 1000000 ) r = 1 ;
                }
            }
        }
    }
}

int main(){
    in2( n , m ) ;
    in2( r , c ) ;
    in2( x , y ) ;
    for( int i = 1 ; i <= n ; i ++ ) {
        scanf( "%s" , ch[ i ] + 1 ) ;
    }
    for( int i = 1 ; i <= n ; i ++ ) {
        for( int j = 1 ; j <= m ; j ++ ) {
            if( ch[ i ][ j ] == '*' ) continue ;
            if(i-1>=1&&ch[i-1][j]=='.') ins1((i-1)*m+j,(i-2)*m+j,0),  ins2((i-1)*m+j,(i-2)*m+j,0);
            if(j-1>=1&&ch[i][j-1]=='.') ins1((i-1)*m+j,(i-1)*m+j-1,1),ins2((i-1)*m+j,(i-1)*m+j-1,0);
            if(i+1<=n&&ch[i+1][j]=='.') ins1((i-1)*m+j,i*m+j,0),      ins2((i-1)*m+j,i*m+j,0);
            if(j+1<=m&&ch[i][j+1]=='.') ins1((i-1)*m+j,(i-1)*m+j+1,0),ins2((i-1)*m+j,(i-1)*m+j+1,1);
        }
    }
    spfa() ;
    spfa2() ;
    int ans = 0 ;
    for( int i = 1 ; i <= n * m ; i ++ ) {
        if( d[ i ] <= x && D[ i ] <= y ) ans ++ ; 
    }
    printf( "%d\n" , ans ) ;
}

牛客寒假算法基礎集訓營6 解題報告