【训练记录】2024年莆田市高中信息学奥赛国庆集训CSP-S提高组(第二天场外)
训练情况
rk#4
\(100 + 100 + 100 + 70 = 370\)
赛后反思
没什么很严重的失误,只是国庆早八起不来,打到后面时间不够做第四题了QAQ,下次一定早起TAT
A题
开场怎么是CF Div4 原题,显然因为 \(a,b,c,d\) 互不相同,最后切出来的结果只有三块或四块,三块的情况是两线没有交叉,四块的情况是两线交叉了,我们考虑从时钟的最外面走一圈,若出现交叉的情况输出 \(4\) 否则输出 \(3\) 即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int a,b,c,d; cin>>a>>b>>c>>d;
string s;
for(int i = 1;i<=12;i++){
if(i == a || i == b) s += "0";
if(i == c || i == d) s += "1";
}
if(s[0] == s[2] && s[1] == s[3]) cout<<4<<endl;
else cout<<3<<endl;
}
signed main(){
freopen("time.in","r",stdin);
freopen("time.out","w",stdout);
int T; cin>>T; while(T--)
solve();
return 0;
}
B题
背包DP缝合题(?)带双重容量+种类01背包,挺板的
DP状态转移方程:
\[dp[pi][li]=max(dp[pi][li],dp[pi-p[i]][li-l[i]]+b[i])
\]
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e3+5;
int n,t,w,k[N];
int p[N],l[N],b[N];
int dp[N][N];
void solve(){
cin>>n>>t>>w;
for(int i=1;i<=n;i++) cin>>k[i];
for(int i=1;i<=n;i++) cin>>p[i]>>l[i]>>b[i];
for(int i=1;i<=n;i++){
while(k[i]--){
for(int pi=w;pi>=p[i];pi--){
for(int li=t;li>=l[i];li--){
dp[pi][li]=max(dp[pi][li],dp[pi-p[i]][li-l[i]]+b[i]);
}
}
}
}
cout<<dp[w][t]<<endl;
}
signed main(){
freopen("hui.in","r",stdin);
freopen("hui.out","w",stdout);
// int T; cin>>T; while(T--)
solve();
}
C题
模拟题,直接模拟即可,注意一下pow会爆,要自己写 \(10^n\) ,右边补一就 n*=10,n++ 即可,左边补 3 就直接加 \(3\times 10^{len+i-1}\)。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
int x;
void solve(){
cin>>x;
int cs = x;
for(int k = 1;k<=cs;k++){
int cnt0 = 0,cnt1 = 0,cnt2 = 0,cnt3 = 0;
int tmp = x;
while(tmp){
if(tmp % 10 == 0) cnt0++;
else if(tmp % 10 == 1) cnt1++;
else if(tmp % 10 == 2) cnt2++;
else if(tmp % 10 == 3) cnt3++;
tmp/=10;
}
for(int i = 1;i<=cnt0;i++) x*=10,x++;
for(int i = 1;i<=cnt1;i++) x*=2;
tmp = x;
int len = 0;
while(tmp)len++,tmp/=10;
for(int i = 1;i<=cnt2;i++){
int add = 3;
for(int j = 1;j<=len+i-1;j++) add *= 10;
x += add;
}
if(cnt3) x += 123456;
x += k;
x %= mod;
// cout<<x<<endl;
}
cout<<x<<endl;
}
signed main(){
freopen("all.in","r",stdin);
freopen("all.out","w",stdout);
// int T; cin>>T; while(T--)
solve();
return 0;
}
D题
双指针题,考虑每一种类的每个人都扔到一个数组里,然后双指针维护即可,种类数 \(< n\) 右边界右移,否则左边界右移,种类数达到 \(n\) 更新答案最小值,极差使用 set 进行维护(要重载运算符)
下面的代码只有 \(70\) 分,不知道挂哪了,待调。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 3;
int n;
int vis[N];
struct node{
int c,t;
bool operator <(const node &x)const{
return t < x.t;
}
};
vector<node> a;
set<node> s;
bool cmp(node x,node y){
return x.t < y.t;
}
void solve(){
cin>>n;
for(int i = 1;i<=n;i++){
int ci; cin>>ci;
for(int j = 1;j<=ci;j++){
int t; cin>>t;
a.push_back((node){i,t});
}
}
sort(a.begin(),a.end(),cmp);
int l = 0,r = 0;
int ans = LONG_LONG_MAX;
int kind = 0;
while(l <= r && r<a.size()){
if(kind<n){
r++;
s.insert(a[r-1]);
vis[a[r-1].c]++;
if(vis[a[r-1].c] == 1) kind++;
} else {
auto it = s.end(); it--;
node ma = *it;
node mi = *s.begin();
ans = min(ans,ma.t - mi.t);
s.erase(a[l]);
vis[a[l].c]--;
if(!vis[a[l].c]) kind--;
l++;
}
}
cout<<ans<<endl;
}
signed main(){
freopen("buhezuo.in","r",stdin);
freopen("buhezuo.out","w",stdout);
// int T; cin>>T; while(T--)
solve();
return 0;
}