2021牛客暑期多校训练营5

2021牛客暑期多校训练营5

B: Boxes

  • 题意

给你n个盒子,每个盒子只有黑白两种情况,而且支付C元就可以知道未开过的盒子中黑盒的个数,问你最少知道每个盒子中球颜色的期望

  • 思路

C(n,i) / (2 ^ n) 拿i个的概率

  1. 不问的情况 (sum w_i)
  2. (w_i)升序排序,C+ (sum w_i(1 - 1/(2 ^ {n - i}))) 求后面有i个全是同色的情况

实在不行可以这样理解, 首先 2 / 16是不用算的, 那么 7 / 8的都要算第一个,并在之后判断是否可行, 那么1个和n-1个盒子不一样的情况,只有2中之后 16 - 2 - 2 / 16 = 3 / 4同上, 之后每种情况2中,那么 (w_1)的贡献就是 7 / 8,(w_2)的贡献是 3 / 4以此类推即可
本题是队友过的,我太菜,呜呜呜
code :

double w[N];

void solve(){
    int n;
    db C;
    cin >> n >> C;
    db sum = 0;
    fep(i,1,n) cin >> w[i], sum += w[i];
    db ans = C;
    sort(w + 1,w + 1 + n);
    for(int i = 1;i <= n;i ++) {
        ans += w[i] * (1 - 1.0 / (pow(2.0, n - i)));
    }
    ans = min(ans, sum);
    printf("%.6f
", ans);
}

D: Double Strings

  • 题意

好的方案的构成是“一段相同的前缀+一个不同字符(a比b小)+长度相同的任意后缀”,求一共多少个好的方案

  • 思路

dp求最长公共子序列,组合数学求后面的即可,其实读懂了题就是简单(sb)题

code :

typedef long long LL;
const int N = 5010, MOD = 1e9+7;
ll fact[N * 2], infact[N * 2];

void init() {
	fact[0] = infact[0] = 1;
    for (int i = 1 ; i < N * 2; ++i) {
        fact[i] = fact[i - 1] * i % MOD;
        infact[i] = infact[i - 1] * pow_mod(i, MOD - 2) % MOD;
    }
}

ll C(ll a,ll b) {
	if(a<0||b<0||a<b) return 0;
	return 1LL * fact[a] * infact[a - b] % MOD * infact[b] % MOD;
}

char a[N], b[N];
ll f[N][N];

void solve(){
    cin >> (a + 1) >> (b + 1);
    ll lena = strlen(a + 1), lenb = strlen(b + 1);
    // 最长公共子序列
    for(int i = 1;i <= lena;i ++) {
        for(int j = 1;j <= lenb;j ++) {
            if(a[i] == b[j]) f[i][j] = (f[i - 1][j] + f[i][j - 1] + 1) % mod;
            else f[i][j] = (f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1]) % mod;
        }
    }
    ll ans = 0;
    for(int i = 1;i <= lena;i ++) {
        for(int j = 1;j <= lenb;j ++) {
            if(a[i] < b[j]) {
                // 最长前缀+当前+任意后缀
                ans = (ans + 1LL * (f[i - 1][j - 1] + 1) % mod * C(lena - i + lenb - j, min(lena - i, lenb - j)) % mod + mod) % mod;
            }
        }
    }
    cout << ans << endl;
}

H: Holding Two

  • 题意

构造一个n行m列的数组,每个数字由0和1组成,每行|竖|斜着连着三个数字是一样的都不行,让你构造

  • 思路

我的构造方法 :
01010101010101
01010101010101
10101010101010
10101010101010
01010101010101
这样交替下去

code :

int main() {
    int n,m;
    cin >> n >> m;
    int cnt = 0;
    for(int i = 1;i <= n;i ++) {
        cnt ++;
        for(int j = 1;j <= m;j ++) {
            if(cnt != 3 && cnt != 4) {
                if(j & 1) {
                    cout << "0";
                }else {
                    cout << "1";
                }
            }else {
                if(j & 1) {
                    cout << "1";
                }else {
                    cout << "0";
                }
            }
        }
        if(cnt == 4) cnt = 0;
        cout << endl;
    }
}

J: Jewels

  • 题意

有n个宝石,三维平面,你位于(0,0,0), 抓一个宝石的消耗为此宝石当前与你的距离的平方(x * x + y * y + z * z ), 你每秒只能抓一个宝石,其他宝石会以(v_i)的速度往下掉

  • 思路

费用流建图,二分图,左边时刻,右边宝石,跑一遍ek,即可,牛客这题卡acwing的费用流模板,调死我了,其实多加个优先队列去优化即可

code :


const int N = 610, M = N * N;
const int INF = 1e9;
int n,m,S,T;
int h[N],e[M],f[M],ne[M],idx;
int incf[N],pre[N];
bool st[N];
ll d[N],w[M],ans = 0;
struct node {
    ll x,y,z,v;
}a[N];

inline void add(int a,int b,int c,ll d){
    e[idx] = b,ne[idx] = h[a],f[idx] = c,w[idx] = d,h[a] = idx ++;
    e[idx] = a,ne[idx] = h[b],f[idx] = 0,w[idx] = -d,h[b] = idx ++;
}

inline bool spfa(){
    for(int i= 0;i < 2 * n + 3;i ++ ) {
        d[i] = 2e18;
        incf[i] = 0;
    }
    priority_queue<pair<ll,int> >q;
    q.push(make_pair(0,S));
    d[S] = 0,incf[S] = INF;
    while(!q.empty()){
        int t = q.top().second;
        q.pop();
        st[t] = false;
        for(int i = h[t];~i;i = ne[i]){
            int j = e[i];
            if(f[i] && d[j] > d[t] + w[i]){
                d[j] = d[t] + w[i];
                pre[j] = i;
                incf[j] = min(f[i],incf[t]);
                if(!st[j]){
                    st[j] = 1;
                    q.push(make_pair(-1LL * d[j],j));
                }
            }
        }
    }
    return incf[T] > 0;
}

ll EK(){
    ll cost = 0;
    while(spfa()){
        ll t = incf[T];
        cost += t * d[T];
        for(int i = T;i != S;i = e[pre[i] ^ 1]){
            f[pre[i]] -= t;
            f[pre[i] ^ 1] += t;
        }
    }
    ans = ans + cost;
    return cost;
}

void solve(){
    scanf("%d", &n);
    for(int i = 1;i <= n;i ++) {
        scanf("%lld%lld%lld%lld", &a[i].x, &a[i].y, &a[i].z, &a[i].v);
        ans += 1LL * a[i].x * a[i].x + a[i].y * a[i].y;
    }
    memset(h,-1,sizeof h);
    S = 2 * n + 2,T = 2 * n + 1;
    for(int i = 0;i < n;i ++) {
        add(S,i,1,0);
    }
    for(int i = n + 1;i <= 2 * n;i ++) {
        add(i,T,1,0);
    }
    for(int i = 0;i < n;i ++) {
        for(int j = n + 1;j <= 2 * n;j ++) {
            add(i,j,1,(a[j - n].z + i * a[j - n].v) * (a[j - n].z + i * a[j - n].v));
        }
    }
    EK();
    printf("%lld
", ans);
}

K: King of Range

  • 题意

给你一个长度为n的序列,让你找到有多少对(l,r) 另 (max(a_l,a_{l+1},···,a_r)) - (min(a_l,a_{l+1},···,a_r))严格大于k,并且给出m次k

  • 思路

单调队列或单调栈或双端队列(队友写过的) 去维护当前最大值最小值的下标,并且维护不能取的数量,每次用n * (n + 1) / 2减去即可

code :


#define int long long
int n,m;
int a[N];

int qa[N],qi[N];

void solve(){
    cin >> n >> m;
    fep(i,1,n) cin >> a[i];
    while(m --) {
        int ans = (n + 1) * n / 2;
        int k;
        int h1 = 0,t1 = -1, h2 = 0, t2 = -1;
        cin >> k;
        int l = 1;
        for(int i = 1;i <= n;i ++) {
            // 记录最大值
            while(h1 <= t1 && a[qa[t1]] < a[i]) t1 --;
            qa[++ t1] = i;
            // 记录最小值
            while(h2 <= t2 && a[qi[t2]] > a[i]) t2 --;
            qi[++ t2] = i;
            while(h1 <= t1 && h2 <= t2 && a[qa[h1]] - a[qi[h2]] > k) {
                // 删除贡献
                if(qa[h1] == l) h1 ++;
                if(qi[h2] == l) h2 ++;
                l ++;
            }
            ans -= (i - l + 1);
        }
        cout << ans << endl;
    }
}
原文地址:https://www.cnblogs.com/darker-wxl/p/15087120.html