The 2023 Guangdong Provincial Collegiate Programming Contest
A - 算法竞赛
#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
int st , n , ed;
cin >> st >> n;
map<int,int> cnt;
for( int i = 1 , x ; i <= n ; i ++ ){
cin >> x;
cnt[x] ++;
}
cin >> ed;
int res = 0;
for( int i = st ; i <= ed ; i ++ ){
if( cnt[i] ) continue;
res ++;
}
cout << res << "\n";
return ;
}
int32_t main() {
int t;
cin >> t;
for( ; t ; t -- ){
solve();
}
return 0;
}
B - 基站建设
\(f(i)\)表示前\(i\)个基站\(i\)必选的情况下的最小成本\(f(i) =\min f(j) + a_i\)
注意\([j+1,i-1]\)中不能有任何一个完整区间,所以我们要计算出\(lim(i)\)表示\([lim(i),i]\)中没有完整区间的最小值。那么\(j\ge lim(i-1)-1\)
然后再用单调队列优化一下dp
#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
// 在 n+1 添加一个花费为 0 的基站,并添加一个 [n+1,n+1] 的需求区间这样 f[n+1] 就是答案
a.push_back(0), n++;
int m;
cin >> m;
vector<vector<int>> b(n+1);
// 里面的负数 -j 表示有一个需求区间是 [i, j]
// 里面是正数 j 表示有一个需求区间是 [j, i]
for (int i = 1, l, r; i <= m; i++)
cin >> l >> r, b[l].push_back(-r), b[r].push_back(l);
b[n].push_back( -n) ,b[n].push_back(n);
vector<int> lim(n+1);
for( int l = 1 , r = 1 , cnt = 0; l <= n ; r ++ ){
for( auto x : b[r] )
if( x > 0 && x >= l ) cnt ++;
while( cnt > 0 && l <= r ){
for( auto x : b[l] )
if( x < 0 && -x <= r ) cnt --;
l ++;
}
lim[r] = l;
}
vector<int> f( n+1 );
deque<int> q;
f[1] = a[1] , q.push_back(0) , q.push_back(1);
for( int r = 2 , l ; r <= n ; r ++ ){
l = lim[r-1] - 1;
while( q.front() < l ) q.pop_front();
f[r] = f[ q.front() ] + a[r];
while( !q.empty() && f[ q.back() ] >= f[r] ) q.pop_back();
q.push_back(r);
}
cout << f[n] << "\n";
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t;
cin >> t;
for (; t; t--)
solve();
return 0;
}
C - 市场交易
按照价格排序后贪心购买
#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
int n;
cin >> n;
vector<pair<int,int>> a(n);
for( auto & [x , y ] : a )
cin >> x >> y;
sort( a.begin(), a.end() );
int res = 0;
for( int l = 0 , r = n-1 , t ; l < r ; ){
t = min( a[l].second , a[r].second );
res += (a[r].first - a[l].first) * t;
a[l].second -= t , a[r].second -= t;
if( a[l].second == 0 ) l ++;
if( a[r].second == 0 ) r --;
}
cout << res << "\n";
return ;
}
int32_t main() {
ios::sync_with_stdio(false) , cin.tie(nullptr) , cout.tie(nullptr);
int t;
cin >> t;
for( ; t ; t -- ){
solve();
}
return 0;
}
D - 新居规划
把人按照\(a_i-b_i\)从小到大排序,排序后越靠前的人越喜欢独居,越靠后的人越喜欢群居,然后分别计算前缀独居贡献和和后缀群居贡献和。最后枚举出独居的人数即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
int n , m;
cin >> n >> m;
vector<pair<int,int>> a(n);
for( auto &[x,y] : a )
cin >> x >> y;
sort( a.begin(), a.end() , [](pair<int,int> x , pair<int,int>y){
return x.first-x.second < y.first - y.second;
} );
vector<int> p(n+1) , q(n+1);
for( int i = 1 ; i <= n ; i ++ )
p[i] = p[i-1] + a[i-1].second;
q[n] = a[n-1].first;
for( int i = n-1 ; i >= 1 ; i -- )
q[i] = q[i+1] + a[i-1].first;
reverse(q.begin()+1, q.end());
int res = 0;
for( int i = 0 , j = n ; i <= n ; i ++ , j -- ){
if( i * 2 + j > m ) break;
if( j < 2 ) continue;
res = max( res , p[i] + q[j] );
}
if( 2 * n - 1 <= m ) res = max( res , p[n] );
cout << res << "\n";
return ;
}
int32_t main() {
ios::sync_with_stdio(false) , cin.tie(nullptr) , cout.tie(nullptr);
int t;
cin >> t;
for( ; t ; t -- ){
solve();
}
return 0;
}
E - 新杯质问
把所有的字符串都插入到 Tire 上,然后贪心的选择即可,首先给每个分支都任一选择一个,这样的话 LCP不会变化。如果不能满足选择的需求的话,就按照字典序贪心的选择,最后一个选到的就是 LCP 新增的一位。
#include <bits/stdc++.h>
using namespace std;
#define int long long
vector<int> val;
struct node {
vector<int> to;
int g, cnt;
node() {
to = vector<int>(26, -1);
cnt = 0, g = -1;
}
};
vector<node> tire;
string res;
void dfs(int x) {
if (tire[x].cnt) val[x] = tire[x].cnt;
for (auto y: tire[x].to) {
if (y == -1) continue;
dfs(y);
val[x] += val[y];
}
return;
}
void calc(int x, int k) {
if (tire[x].g != -1) res += (char) (tire[x].g + 'a');
if( tire[x].cnt ) k -= tire[x].cnt;
if( k <= 1 ) return ;
int cnt = 26;
for (auto y: tire[x].to)
cnt -= (y == -1);
if (cnt >= k) return;
for (auto y: tire[x].to) {
if (y == -1) continue;
cnt--;
if (cnt + val[y] < k) k -= val[y];
else {
k -= cnt;
calc(y, k);
return;
}
}
}
void solve() {
int n, m;
cin >> n >> m;
tire = vector<node>(1, node());
for (int i = 1, t; i <= n; i++) {
string s;
cin >> s;
t = 0;
for (int i = 0, j; i < s.size(); i++) {
j = s[i] - 'a';
if (tire[t].to[j] == -1) {
tire[t].to[j] = tire.size();
tire.push_back(node());
tire.back().g = j;
}
t = tire[t].to[j];
}
tire[t].cnt ++;
}
val = vector<int>(tire.size());
dfs(0);
res = "";
calc(0, m);
if (res == "") res = "EMPTY";
cout << res << "\n";
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
cin >> t;
for (; t; t--) {
solve();
}
return 0;
}
I - 路径规划
二分答案,二分\(mex\)的值,则\([0,mex-1]\)都应该在序列中,这样这些点的下标按照\(x\)升序排列后,\(y\)也应该是升序排序。所以\(O(n\log n )\)的校验即可
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, m;
cin >> n >> m;
vector<pair<int, int>> a(n * m);
for( int i = 1 ; i <= n ; i ++ )
for( int x , j = 1 ; j <= m ; j ++ )
cin >> x , a[x] = make_pair( i , j );
auto check =[a]( int x ){
if( x < 0 ) return true;
auto b = vector<pair<int,int>>( a.begin() , a.begin() + x );
sort( b.begin() , b.end() );
for( int i = 1 ; i < b.size() ; i ++ )
if( b[i].second < b[i-1].second ) return false;
return true;
};
int l = 0 , r = n*m , mid ,res = 0;
while( l <= r ){
mid = ( l + r ) >> 1;
if( check( mid ) ) res = mid , l = mid + 1;
else r = mid - 1;
}
cout << res << "\n";
return;
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
cin >> t;
for (; t; t--) {
solve();
}
return 0;
}
K - 独立钻石
直接 dfs,暴力的枚举棋子和方向即可,\(O(6!\times 4)\)
#include <bits/stdc++.h>
using namespace std;
int res, n, m;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
void dfs(int k, vector<vector<int>> g) {
res = min(res, k);
if( k == 1 ) return ;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (g[i][j] == 0) continue;
for (int l = 0, ax, ay, bx, by; l < 4; l++) {
ax = i + dx[l], ay = j + dy[l];
if (ax < 0 || ay < 0 || ax >= n || ay >= m || g[ax][ay] == 0) continue;
bx = ax + dx[l], by = ay + dy[l];
if (bx < 0 || by < 0 || bx >= n || by >= m || g[bx][by] == 1) continue;
g[i][j] = 0, g[ax][ay] = 0, g[bx][by] = 1;
dfs(k-1, g);
g[i][j] = 1, g[ax][ay] = 1, g[bx][by] = 0;
}
}
}
}
void solve() {
int k;
cin >> n >> m >> k;
vector<vector<int>> g(n, vector<int>(m, 0));
res = k;
for (int x, y; k; k--)
cin >> x >> y , x -- , y -- , g[x][y] = 1;
dfs(res, g);
cout << res << "\n";
return;
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
cin >> t;
for (; t; t--) {
solve();
}
return 0;
}