USACO 2023-2024 赛季复盘
【USACO23DEC】Cu 复盘
我先用了一个号打,然后是 T1 TLE * 4,T2 AC,T3 WA * 2。然后后面开了一个号调了一些,喜提阿克。
T1:
首先我们知道,对于 \(a_1\) 每一次是最先吃糖果的。
所以必然有两个情况:
- 全部给他吃了
- 吃了一些
首先情况一,我们可以直接特判掉。
剩下情况二,吃了一些没吃完代表:吃了自己的高度,相当于身高乘2。于是我们只需要进行 \(\log 10^9\) 次。然后后面直接 \(O(n)\) 暴力。时间复杂度 \(O(n \log 10^9)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
#define int long long
int n,m;
int a[N],b[N];
void solve(){
cin >> n >> m;
for(int i = 1;i <= n;i++)
cin >> a[i];
for(int i = 1;i <= m;i++)
cin >> b[i];
for(int i = 1;i <= m;i++){
if(b[i] <= a[1]){
a[1]+=b[i];
continue;
}
int tmp = 1;
for(int j = 1;j <= n;j++)
if(a[j] >= tmp && tmp <= b[i]){
int t=a[j]+1;
a[j] += min(a[j]-tmp+1,b[i]-tmp+1);
tmp = t;
}
}
for(int i = 1;i <= n;i++)
cout<<a[i]<<endl;
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
solve();
return 0;
}
T2
这是我第一次唯一场切的一道题:
首先我在这题上写了三份代码。原因是我一开始想到分讨,写复杂了。
根据贪心策略,很容易知道:天数越大,奶牛数少,所以我们让天数最多。
然后我们会发现头尾的那个区间如果是 \(x\),那么相当于一个 \(2 \times x - 1\) 的一个普通区间(就是在中间的那种区间)。
所以我们根据处理后的每一个区间的长度进行计算,然后得到了每一个区间最小的天数,也就是我们能够放最多的天数。
所以按照这个去模拟即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n;
int xx=-1,yy=-1;
string s;
vector<pair<int,bool> > v;
int res,ok=1,cnt=0;
int minn = 1e9;
void solve(){
cin >> n >> s;
s = ' '+s;
for(int i = 1;i <= n;i++){
if(s[i]=='0'){
if(res)v.push_back({res,ok});
ok=0;
res=0;
}else if(s[i]=='1'){
res++;cnt++;
}
}
if(res) v.push_back({res,1});
int min2=1e9;
for(auto it:v){
if(it.second == 0)min2 = min(min2,it.first);
else{
min2 = min(min2,it.first * 2-1);
if(xx == -1)
xx = it.first;
else
yy = it.first;
}
}
minn=(min2+1)/2;
int res=0;
for(auto it:v){
if(it.first % (minn * 2 - 1) == 0) res+=it.first / (minn * 2 - 1);
else res+=it.first / (minn * 2 - 1) + 1;
}
cout<<res;
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
solve();
return 0;
}
T3
高级的说:解个不等式,低级的说:前缀和。
首先,我们先将所有 \(a\) 变换位置到最终应该在的位置。
然后,我们可以通过 \(a_i\) 和 \(a_{i-1}\) 的高度差和速度差,算出来他们之间还需要多久可以符合要求,或者是多久就不符合要求了。拿出草稿纸,自己列举四种情况分别讨论。
如果这个点开始合法,那么在这里进行标记,如果这里开始不合法,也搞一个标记。最后一遍前缀和,结束。
我被这道题卡了很久的问题是一些细节没有处理好。一些边界少加一,多加一的。随后在自己手搓 hack 下,终于过掉啦啊。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
int n,t[N];
struct node{
int h,a;
}a[N],c[N];
bool cmp(pair<int,bool> a,pair<int,bool> b){
if(a.first == b.first)
return a.second > b.second;
return a.first < b.first;
}
bool check(){
for(int i = 2;i <= n;i++) if(a[i].h<=a[i-1].h) return false;
return true;
}
vector<pair<int,bool> > v;
bool solve(){
v.clear();
//1为开始,0为结束。
cin >> n;
for(int i = 1;i <= n;i++) {
cin >> a[i].h;
}
for(int i = 1;i <= n;i++) {
cin >> a[i].a;
}
for(int i = 1;i <= n;i++) {
cin >> t[i];
}
if(n==1){
cout<<0<<endl;
return true;
}
for(int i = 1;i <= n;i++){
c[n-t[i]] = a[i];
}
for(int i = 1;i <= n;i++){
a[i] = c[i];
}
if(check()){
cout<<0<<endl;
return true;
}
int res=0;
for(int i = 2;i <= n;i++){
if(a[i-1].h > a[i].h) {
if(a[i].a <= a[i-1].a) {
return false;
}
v.push_back({((a[i-1].h - a[i].h) / (a[i].a-a[i-1].a) + 1),1});
}else if(a[i-1].h < a[i].h){
if(a[i].a >= a[i-1].a) v.push_back({0,1});
else {
v.push_back({(a[i].h - a[i-1].h) % (a[i-1].a-a[i].a) == 0?(a[i].h - a[i-1].h) / (a[i-1].a-a[i].a):((a[i].h - a[i-1].h) / (a[i-1].a-a[i].a)+1),0});
res++;
}
}else{
if(a[i].a > a[i-1].a) v.push_back({0,1});
else return false;
}
}
sort(v.begin(),v.end(),cmp);
// for(auto it:v){
// cout<<it.first<<" "<<it.second<<endl;
// }
for(int it=0;it < v.size();it++){
if(v[it].second == 1)
res++;
else
res--;
if(res == n-1 && v[it+1].first != v[it].first) {
cout<<v[it].first<<endl;
return true;
}
}
return false;
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--){
if(!solve()) cout<<-1<<endl;
}
return 0;
}
【USACO24JAN】Cu 复盘
T1
一开始我试着用双指针,但发现好像写不了。
后面发现恍然大悟。这道题我们可以手玩一定长度的数组。假设我们只有两种情况 \(1\) 代表的是相同数字的数,\(0\) 代表的是不同数字的数。
我们会发现只有形如 \(1,1\) 或 \(1,0,1\) 的子数组是最基础的形式,由这样可以变成 \(1,1,1\),\(1,1,1,1\) 等知道填满数组,这样,就可以让所有的数字统一了。这也达到了目的。
所以我们只需要记录哪些数 \(a_i = a_{i+1}\) 或 \(a_i = a_{i+2}\)。用 set 记录。
然后 set 自动排序和去重,直接输出即可。
但注意的是,你可能也有这样的经历!
你会觉得很逆天,但是“每一行后不能加空格”。这样,我们又愚蠢的把 set 转成 vector,判断是不是到末尾,是的话就不加空格。
话说 USACO 这波真的很逆天。
T2
我们发现直接模拟就好了,在 dfs 中,会有很多个参数。例如:
- \(x\) 代表当前位置。
- \(d\) 代表弹跳的力气。
- \(f\) 代表方向。
- \(cnt\) 代表砸烂了多少个。
我们对 dfs 进行剪枝即可。四个参数可以开一个数组来记忆化,但是空间会炸,所以开一个 map。
map<pair<pair<int,int>,pair<int,int> >,bool> mp;
这个记忆化只用记录是否走到,所以是一个 bool 类型。
这样,我们直接暴力模拟即可。轻松 AC!
T3
因为对于 \(i\) 进行操作,肯定会影响到后面的数,因此得先改前面的数再改后面的数,为了线性,不妨对 \(a\) 数组进行差分。
我们实时维护两个变量。
\(sum\) 数组是此时进行完操作后,之后会有多少增加细菌的个数。
然而 \(ans\) 维护的是答案,每一次会加上 \(d_i + sum\) 的绝对值。\(d_i\) 是差分数组。
时间复杂度 \(O(n)\)。
本文来自博客园,作者:gsczl71,转载请注明原文链接:https://www.cnblogs.com/gsczl71/p/17909469.html
gsczl71 AK IOI!RP = INF 2024年拿下七级勾!