SMU Spring 2023 Trial Contest Round 2

A - 生活大爆炸版石头剪刀布

B - 联合权值

C - 飞扬的小鸟

D - 无线网络发射器选址

E - 寻找道路

F - 廊桥分配

G - 格雷码

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;
}
posted @ 2023-03-07 22:53  PHarr  阅读(17)  评论(0编辑  收藏  举报