【训练记录】山东济南齐鲁工业大学ACM集训队第二次入队赛同步赛(场外VP)
https://icpc.qlu.edu.cn/contest/66ed8b746002253a77c10d5e
训练情况
场外 rk#2 AK
赛后反思
A题太菜了,没看出来是01背包DP,往前缀和上面想了,写了个假做法。
B题又不认真看题,忘记了 \(= 0\) 的情况。
C题博弈论乱猜
D题未考虑完全导致一次WA
A题
分两组,两组和相同,观察数据范围我们考虑 01 背包,先求出所有的和。显然,如果和为奇数则无法分组,当为偶数的时候,判断能否取出 \(\frac{sum}{2}\) 即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e7 + 3;
int dp[N];
void solve(){
int n; cin>>n;
vector<int> a(n + 1);
int sum = 0;
dp[0] = 1;
for(int i = 1;i<=n;i++) cin>>a[i],sum+=a[i];
if(sum&1){
cout<<"No"<<endl;
return;
}
for(int i = 1;i<=n;i++){
for(int j = sum/2;~j;j--){
if(j-a[i] < 0) continue;
if(dp[j-a[i]]) dp[j] = dp[j-a[i]];
}
}
if(dp[sum/2]) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
B题
找没被区间覆盖的点,考虑差分了再前缀和,坑点:\(0\) 也算。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 3;
int a[N];
void solve(){
int n,m; cin>>n>>m;
for(int i = 1;i<=m;i++){
int l,r; cin>>l>>r;
a[l]++,a[r+1]--;
}
for(int i = 1;i<=n;i++) a[i]+=a[i-1];
int ans = 0;
for(int i = 0;i<=n;i++) if(!a[i]) ans++;
cout<<ans<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
C题
逆天我不会的博弈论,逆天后手必胜的结论,通过玩 \(n \le 4\) 的样例,大胆猜测后手必胜
我又会了,后手有一个必胜策略,就是跟着先手的人走中心对称的那条边,最后出现一个正方形三条边的情况,直接去占即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
cout<<"Tianmingren"<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
D题
考虑什么时候走不到 \(n\),当某一位为零的时候,并且前面的点没有办法跨过这个 \(0\)。维护一个最右的边界,同时遇到 \(0\) 判断即可,如果无法跨过答案就是 No,否则 Yes。
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int n; cin>>n;
vector<int> a(n + 1);
for(int i = 1;i<=n;i++) cin>>a[i];
int last = 0;
for(int i = 1;i<n;i++){
if(a[i]) last = max(last,i+a[i]-1);
if(!a[i] && last < i){
cout<<"No"<<endl;
return;
}
}
cout<<"Yes"<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
E题
快读模板 + %99
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int s = 0; char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)){
s = (s << 1) + (s << 3) + (c ^ 48);
s%=99;
c = getchar();
}
return s;
}
int ans = 0;
void solve(){
int x; x = read();
if(!x) ans++;
}
signed main(){
int T; cin>>T; while(T--)
solve();
cout<<ans<<endl;
return 0;
}
F题
模拟题,我们发现字母的位置是两倍的 \(i\) 或 \(j\),其他都是 # 号。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 8007;
char ans[N][N];
void solve(){
int n,m; cin>>n>>m;
vector<string> s(n + 1);
for(int i = 0;i<n;i++) cin>>s[i];
for(int i = 0;i<n;i++){
for(int j = 0;j<m;j++){
if(s[i][j] >= 'A' && s[i][j] <= 'Z') s[i][j]+=32;
else s[i][j] -= 32;
}
}
for(int i = 0;i<n;i++){
for(int j = 0;j<m;j++){
ans[(i+1)*2][(j+1)*2] = s[i][j];
}
}
for(int i = 1;i<=2*n+1;i++){
for(int j = 1;j<=2*m+1;j++){
if(!ans[i][j]) ans[i][j] = '#';
}
}
for(int i = 1;i<=n*2+1;i++){
for(int j = 1;j<=m*2+1;j++){
cout<<ans[i][j];
}
cout<<endl;
}
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
G题
单调不降,我们可以发现减少前面的较大值比增加后面的较小值要更优,减小前面的最大值会让数列变得更容易单调不降,维护一个当前的最大值(考虑 \(O(nlogn)\) 优先队列),若出现比他还小的替换掉当前最大值更新答案即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
priority_queue<int> q;
int ans;
void solve(){
int x; cin>>x;
q.push(x);
if(x<q.top()){
ans+=q.top()-x;
q.pop();
q.push(x);
}
}
signed main(){
int T; cin>>T; while(T--)
solve();
cout<<ans<<endl;
return 0;
}
H题
天数要向上取整,观察到取模有很好的周期性,把星期都偏移到 \(0 \sim 6\) 上,然后取模即可,然后再加回来
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int x,y,a; cin>>x>>y>>a;
int add = (x+(y-1))/y;
a--;
a+=add;
a%=7;
a++;
cout<<a<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}