女神训练赛 Round01

Ball

对于没有一个球只有两种拜访的方式,我们可以直接枚举一下每个球放在那里,然后判断一下,因为只有10个球所以一种只有1024种情况,这种做法在复杂度上是可以接受的。

但是我们可以做一点小优化,就是用dfs来枚举一下球放在那里,同时在枚举的过程种判断一下前缀是否合法,这样可以大大的减少枚举的次数。

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

int read(){...}

int a[15] , flag;
// flag 表示时候找到合法方案

void dfs( int t , int x , int y ){
    if( flag ) return;// 已经找到合法方案
    if( t > 10 ){
        cout << "YES\n";
        flag = 1;
        return;
    }
    if( a[t] > x ) dfs( t + 1 , a[t] , y );
    if( a[t] > y ) dfs( t + 1 , x , a[t] );
    return;
}

void solve(){
    flag = 0;
    for( int i = 1 ; i <= 10 ; i ++ )
        a[i] = read();
    dfs( 1 , 0 , 0 );
    if( !flag ) cout << "NO\n";// 找不到合法方案
}

int main(){
    for( int T = read() ; T ; T -- )
        solve();
}

Moo University - Financial Aid

首先我们把所有的物品按照价值排序,逐个枚举每一个物品i做中位数,然后计算前[1,i-1]n/2个,后[i+1,c]n/2个的最小值。

如何计算前前缀的最小值呢?可以用堆类维护,按照顺序每次加入一个物品,然后在删掉对顶的物品,这样保证了堆的大小始终是n/2,那么此时堆中的物品之和就是前缀最小值。后缀类似。

#include<iostream>
#include <queue>
#include <algorithm>
#define int long long
using namespace std;

typedef pair<int,int> PII;

const int N = 1e5+5;
PII a[N];
int n , c , f , m  , sufSum[N] , preSum[N] , res = -1;

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

priority_queue< int > q;

int32_t main() {
    n = read() , c = read() , f = read() , m = n / 2;
    for( int i = 1 ; i <= c ; i ++ )
        a[i].first = read() , a[i].second = read();
    sort( a+1 , a+1+c );
    for( int i = 1 , cnt = 0 ; i <= c ; i ++ ){
        if( q.size() == m ) preSum[i] = cnt;
        else preSum[i] = INT_MAX;
        q.push( a[i].second ) , cnt += a[i].second;
        while ( q.size() > m ) cnt -= q.top() , q.pop();
    }
    while(q.size()) q.pop();// 清空
    for( int i = c , cnt = 0 ; i >= 1 ; i -- ){
        if( q.size() == m ) sufSum[i] = cnt;
        else sufSum[i] = INT_MAX;
        q.push( a[i].second ) , cnt += a[i].second;
        while ( q.size() > m ) cnt -= q.top() , q.pop();
    }
    for( int i = c ; i >= 1 ; i -- )
        if( preSum[i] + sufSum[i] + a[i].second <= f ){
            res = a[i].first;
            break;
        }
    cout << res << "\n";
    return 0;
}
 


Marked Ancestor

这道题可以用并查集来做,但是不路径压缩就好了,应该是全场最容易想的一道题目

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

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

const int N = 1e5+5;
int n , m , fa[N];

int getFa( int x ){
    if( fa[x] == x ) return x;
    return getFa( fa[x] );
}

void solve(){
    n = read() , m = read();
    if( n == 0 && m == 0 ) exit(0);
    fa[1] = 1;
    for( int i = 2 ; i <= n ; i ++ ) fa[i] = read();
    long long res = 0;
    char op ;
    int x ;
    for(  ; m ; m -- ){
        cin >> op , x = read();
        if( op == 'Q' ) res += getFa( x );
        else fa[x] = x;
    }
    cout << res << "\n";
}

int main(){
    while(1)
        solve();
}

do NOT a=2b

首先数字是可以重复的,但是数据的范围只有1e6,所以可以用一个数组v[i]记录每个数字i出现了多少次,自然删掉数字i的代价就是v[i]

现在考虑如果数字i删掉了,那么数字i/2i*2就不用删掉,反之i/2i*2都需要删掉。

这样我们就发现可以把1e6个数字分成5e5个序列,每一个序列都是奇数开头,然后公比为 2 的等比数列。对于每一个序列必须间隔删除,而且容易就会发现不同的序列之间是不会有相同的数列的。那么实际上就是对每个序列做 dp就好了。

f[i][0/1]表示第i个数在保证序列合法的情况下当前位删除(1)或不删除(0)的最小花费。

f[i][0]=f[i/2][1]当前数不删除前一个数就必须删除。

f[i][1]=min( f[i/2][1] , f[i/2][0] )+v[i],当前数删掉前一个数删不删就无所谓。

这样我们只需要统计每一个序列的最后一位的min( f[i][0] , f[i][1])之和就好。

那么序列的最后一位都是那些数呢?显然就是[n/2+1,n]中的每一个数。

#include<bits/stdc++.h>

using namespace std;

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
const int N = 1e6;
int f[N+5][2] , v[N+5];

int32_t main() {
    for( int n = read() , x ; n ; n -- )
        x = read() , v[x]++;

    for( int i = 1 ; i <= N ; i ++ ){
        if( i&1 ){
            f[i][0] = 0;
            f[i][1] = v[i];
        }
        else{
            f[i][0] = f[i/2][1];
            f[i][1] = min( f[i/2][1] , f[i/2][0] ) + v[i];
        }
    }
    
    int res = 0;
    for( int i = N / 2 + 1 ; i <= N ; i ++ )
        res += min( f[i][0] , f[i][1] );
    cout << res << "\n";
}
posted @ 2022-10-01 21:27  PHarr  阅读(21)  评论(0编辑  收藏  举报