SMU Spring 2023 Trial Contest Round 2
A - 生活大爆炸版石头剪刀布
这套题就是注意处理一下输赢关系就好了
#include <bits/stdc++.h>
using namespace std;
int check( int x , int y ){
if( x == y ) return 0;
if( x == 0 ){
if( y == 1 || y == 4 ) return -1;
return 1;
}else if( x == 1 ){
if( y == 2 || y == 4 ) return -1;
return 1;
}else if( x == 2 ){
if( y == 0 || y == 3 ) return -1;
return 1;
}else if( x == 3 ){
if( y == 1 || y == 0 ) return -1;
return 1;
}else{
if( y == 2 || y == 3 ) return -1;
return 1;
}
}
int main(){
int n , na , nb;
cin >> n >> na >> nb;
vector<int> a(na) , b(nb);
for( auto &i : a ) cin >> i;
for( auto &i : b ) cin >> i;
int cnta = 0 , cntb = 0;
for( int i = 0 , t ; i < n ; i ++ ){
t = check( a[ i%na ] , b[ i%nb ] );
if( t == 1 ) cnta ++;
if( t == -1 ) cntb ++;
}
cout << cnta << " " << cntb << "\n";
return 0;
}
B - 联合权值
任意点相邻点之前的最短距离一定是\(2\),这是因为是一个图。
一个点相邻的点的权值是\(w_i\),所以最大值就是最大和次大值相乘。令\(W=\sum w_i\),则联合权值和就是\(\sum w_i(W-w_i)\)
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read(){
int x = 0 , 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;
}
const int mod = 10007;
int32_t main(){
int n = read() , res = 0 , sum = 0;
vector<vector<int>> e(n+1);
vector<int> w(n+1);
for( int u , v , i = n-1 ; i ; i -- )
u = read() , v = read() , e[u].push_back(v) , e[v].push_back(u);
for( int i = 1 ; i <= n ; i ++ ) w[i] = read();
for( int i = 1 , a , b , cnt; i <= n ; i ++ ){
a = 0 , b = 0 , cnt = 0;
for( auto j : e[i] ){
cnt = (cnt + w[j]) % mod;
if( w[j] >= a ) b = a , a = w[j];
else b = max(b , w[j]);
}
res = max( res , a * b );
for( auto j : e[i] ){
cnt = ( cnt - w[j] + mod ) % mod;
sum = ( sum + cnt * w[j] ) % mod;
}
}
cout << res << " " << sum * 2 % mod << "\n";
return 0;
}
C - 飞扬的小鸟(补题)
dp题目,注意点就是在每个位置其实可以无限次向上飞的,但是高度最高只能到m
f[i][j]
表示飞到i
高度j
的最少向上飞的次数,用正无穷表示无法到达
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read(){
int x = 0 , 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;
}
int32_t main(){
int n = read() , m = read() , k = read();
vector<int> up(n+1) , down(n+1);
for( int i = 1 ; i <= n ; i ++ ) up[i] = read() , down[i] = read();
vector<int> lower(n+1, 1 ) , upper(n+1, m) , pipeline;
for( int a , b , c ; k ; k -- )
a = read(), b = read(), c = read(), lower[a] = b + 1, upper[a] = c - 1, pipeline.push_back(a);
vector<vector<int>> f( n+1 , vector<int>( m+1 , INT_MAX ) );
for( int i = 1 ; i <= m ; i ++ ) f[0][i] = 0;
for( int i = 1 ; i <= n ; i ++ ){
for( int j = up[i]+1 ; j <= m ; j ++ )
f[i][j] = min( f[i-1][j-up[i]] + 1 , f[i][j-up[i]] + 1 );
for( int j = m+1 ; j <= m+up[i] ; j ++ )
f[i][m] = min( {f[i][m] , f[i-1][j-up[i]] + 1, f[i][j-up[i]] + 1 } );
for( int j = 1 ; j <= m-down[i] ; j ++ )
f[i][j] = min( f[i][j] , f[i-1][j+down[i]] );
for( int j = 1 ; j < lower[i] ; j ++ ) f[i][j] = INT_MAX;
for( int j = upper[i]+1 ; j <= m ; j ++ ) f[i][j] = INT_MAX;
}
int res = INT_MAX;
for( int i = 1 ; i <= m ; i ++ )
res = min( res , f[n][i] );
if( res < INT_MAX ) return cout << "1\n" << res <<"\n" , 0;
sort(pipeline.begin(), pipeline.end());
int cnt = 0;
for( auto i : pipeline ){
int flag = 0;
for( int j = 1 ; j <= m ; j ++ )
if( f[i][j] < INT_MAX ){
flag = 1;
break;
}
cnt += flag;
}
cout << "0\n" << cnt << "\n";
return 0;
}
D - 无线网络发射器选址
二维前缀和,直接枚举中心点的位置就好了
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 150;
int f[N][N];
int d , n , res = -1 , cnt = 0;
int32_t main(){
cin >> d >> n;
for( int x , y , k ; n ; n -- ){
cin >> x >> y >> k;
x ++ , y ++;
f[x][y] += k;
}
for( int i = 1 ; i <= 129 ; i ++ )
for( int j = 1 ; j <= 129 ; j ++ )
f[i][j] = f[i][j] + f[i-1][j] + f[i][j-1] - f[i-1][j-1];
for( int i = 1 , t ; i <= 129 ; i ++ )
for( int j = 1 , x , y , a , b ; j <= 129 ; j ++ ){
x = max( 1ll , i-d ) , y = max( 1ll , j-d );
a = min( 129ll , i+d ) , b = min( 129ll , j+d );
t = f[a][b] - f[a][y-1] - f[x-1][b] + f[x-1][y-1];
if( t > res ) res = t , cnt = 1;
else if( t == res ) cnt ++;
}
cout << cnt << " " << res;
return 0;
}
E - 寻找道路
首先反向建图,然后从终点开始找出所有可以到达终点的点。然后再预处理出所有满足条件 1 的点。最后跑 Dijkstra 就行。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 150;
int f[N][N];
int d , n , res = -1 , cnt = 0;
int read(){
int x = 0 , 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;
}
int32_t main(){
int n = read() , m = read();
vector< vector<int> > e(n+1) , g(n+1);
for( int u , v ; m ; m -- )
u = read() , v = read() , e[u].push_back(v) , g[v].push_back(u);
int s = read() , t = read();
vector<bool> tag(n+1 , false);
queue<int> q;
q.push(t);
while( !q.empty() ){
int u = q.front(); q.pop();
if( tag[u] ) continue;
tag[u] = true;
for( auto v : g[u] ){
if( tag[v] ) continue;
q.push(v);
}
}
vector<bool> can(n+1 , true );
for( int i = 1 ; i <= n ; i ++ )
for( auto v : e[i] )
if( tag[v] == false ) {
can[i] = false;
break;
}
vector<bool> vis(n+1 , false);
vector<int> dis(n+1 , INT_MAX );
priority_queue< pair<int,int> , vector<pair<int,int>> , greater<pair<int,int>> > heap;
dis[s] = 0 , heap.emplace(0 , s);
while( !heap.empty() ){
auto [ d , u ] = heap.top(); heap.pop();
if( vis[u] ) continue;
vis[u] = true;
if( can[u] == false ) continue;
for( auto v : e[u] ){
if( vis[v] || can[v] == false ) continue;
if( dis[v] <= d + 1 ) continue;
dis[v] = d + 1 , heap.emplace( dis[v] , v );
}
}
if( dis[t] == INT_MAX ) dis[t] = -1;
cout << dis[t];
return 0;
}
F - 廊桥分配(补题)
45%做法
枚举廊桥的分配方案,然后用堆来计算可以停靠的飞机总数
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 150;
int f[N][N];
int d , n , res = -1 , cnt = 0;
int read(){
int x = 0 , 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;
}
int32_t main(){
freopen("airport.in","r",stdin);
freopen("airport.out","w",stdout);
int n = read() , m1 = read() , m2 = read();
vector<pair<int,int>> a(m1) , b(m2);
for( auto &[l,r] : a ) l = read() , r = read();
for( auto &[l,r] : b ) l = read() , r = read();
sort(a.begin(), a.end()) ,sort(b.begin(), b.end());
int res = 0;
priority_queue<int,vector<int> , greater<int>> q;
for( int i = 0 , j , cnt ; i <= n ; i ++ ){
j = n-i , cnt = 0;
q = priority_queue<int,vector<int> , greater<int>>();
for(auto [l,r] : a ){
while( !q.empty() && q.top() <= l ) q.pop();
if( q.size() + 1 <= i ) q.push(r) , cnt ++;
}
q = priority_queue<int,vector<int> , greater<int>>();
for( auto [l,r] : b ){
while( !q.empty() && q.top() <= l ) q.pop();
if( q.size() + 1 <= j ) q.push(r) , cnt ++;
}
res = max( res , cnt );
}
cout << res << "\n";
return 0;
}
AC
改变一下贪心策略,在同时有多个廊桥可以停靠时优先选择标号小的。然后统计出每个廊桥停靠的飞机总数,在求前缀和就得到了如果分配了\(i\)个廊桥最多可以停靠多少飞机。把国内国际都处理完后,在枚举分配情况,\(O(1)\)计算最多可以停靠的飞机总数即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read(){
int x = 0 , 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;
}
int32_t main(){
freopen("airport.in","r",stdin);
freopen("airport.out","w",stdout);
int n = read() , m1 = read() , m2 = read();
vector<pair<int,int>> a(m1) , b(m2);
for( auto &[l,r] : a ) l = read() , r = read();
for( auto &[l,r] : b ) l = read() , r = read();
sort(a.begin(), a.end()) ,sort(b.begin(), b.end());
vector<int> resA(n+1);
priority_queue< pair<int,int> , vector<pair<int,int>> , greater<pair<int,int>> > lq;
priority_queue< int , vector<int> , greater<int> > wq;
for( auto i = 1 ; i <= n ; i ++ ) wq.push(i);
for( auto [l,r] : a ){
while( !lq.empty() && lq.top().first <= l ) wq.push( lq.top().second ) , lq.pop();
if( wq.empty() ) continue;
lq.emplace( r , wq.top() ) , resA[wq.top()] ++ , wq.pop();
}
vector<int> resB(n+1);
lq = priority_queue< pair<int,int> , vector<pair<int,int>> , greater<pair<int,int>> >();
wq = priority_queue< int , vector<int> , greater<int> >();
for( auto i = 1 ; i <= n ; i ++ ) wq.push(i);
for( auto [l,r] : b ){
while( !lq.empty() && lq.top().first <= l ) wq.push( lq.top().second ) , lq.pop();
if( wq.empty() ) continue;
lq.emplace( r , wq.top() ) , resB[wq.top()] ++ , wq.pop();
}
for( int i = 1 ; i <= n ; i ++ ) resA[i] += resA[i-1] , resB[i] += resB[i-1];
int res = 0;
for( int i = 0 , j ; i <= n ; i ++ )
j = n - i , res = max( res , resA[i] + resB[j] );
cout << res << "\n";
return 0;
}
G - 格雷码(补题)
50%做法
直接暴力出结果
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 150;
int f[N][N];
int d , n , res = -1 , cnt = 0;
int read(){
int x = 0 , 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;
}
int32_t main(){
freopen("code.in","r",stdin);
freopen("code.out","w",stdout);
int n = read() , k = read();
if( n == 1 ) {
cout << k << "\n";
return 0;
}
vector<string> a , b;
a.push_back("0") , a.push_back("1");
for( int i = 2 ; i <= n ; i ++ ){
b.clear();
for( auto s : a ) b.push_back( "0" + s );
reverse(a.begin(),a.end());
for( auto s : a ) b.push_back( "1" + s );
a = b;
}
cout << a[k] << "\n";
return 0;
}
AC
计算出第\(n\)位长度,判断是在前半部分还是后半部分,如果在前半部分就是补一个 0,否则就补一个 1。
设第\(n\)位长度数\(p\),如果实在后半部分的话,实际上是在后半部分的\(k-\frac p 2\)的,但是以为后半部分是要把序列逆序,所以下一步实际上应该是第\(\frac p 2-(k-\frac p 2 )= p - k\)个格雷码
#include <bits/stdc++.h>
using namespace std;
#define int __int128
typedef long long ll;
int read(){
int x = 0 , 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;
}
int32_t main(){
freopen("code.in","r",stdin);
freopen("code.out","w",stdout);
int n = read() , k = read();
string s = "";
int p = ((int)1 << n ) - 1 , mid = p >> 1;
while( true ){
if( n == 1 ){
if( k == 1 ) s += "1";
else s += "0";
break;
}
if( k <= mid ) s += "0";
else k = p - k , s += "1";
n -- , p >>= 1 , mid >>= 1;
}
cout << s;
return 0;
}