女神训练赛 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/2
和i*2
就不用删掉,反之i/2
和i*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";
}