2021 四川省赛vp 题解
省流:没有 C F G
2021年四川省赛 vp
官方的信息:https://sua.ac/problems.html ,有题解、榜单、题面
五一集训第二天的模拟赛,这套题打起来真的挺爽,全程划水的时间比较少,基本都在想题,最后卡题的时候也是充满希望地在想题,不会说一点都想不出来那种
不过这场比赛打下来,我觉得我真的要好好地督促队友来机房训练了!
A. Chuanpai#
直接暴力就行,a 和 b 都很小,求 的方案数
#include <iostream>
using namespace std;
int main()
{
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
int ans = 0;
for(int i=1; i<=6; i++)
if(n - i >= i && n - i >= 1 && n - i <= 6)
ans++;
cout << ans << endl;
}
return 0;
}
B. Hotpot#
这题一开始分喜欢一个菜的人数有奇数和偶数的情况讨论,发现如果是偶数的话,一轮之后会直接回到原地,如果是奇数的话两轮之后会直接回到原地
所以考虑直接模拟跑两轮作为一个周期,然后答案乘上周期数,剩下那一点余数直接暴力跑一边就行
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
const int inf = 2147483647;
int num[maxn], alp[maxn], ans[maxn];
int n, k, m;
void solve(int x)
{
for(int i=0; i<=k; i++) alp[i] = 0;
int tp = 0;
while(x--)
{
if(alp[num[tp]])
{
alp[num[tp]] = 0;
ans[tp]++;
}
else
alp[num[tp]]++;
tp++;
if(tp == n) tp = 0;
}
}
signed main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d %d %d", &n, &k, &m);
for(int i=0; i<n; i++) scanf("%d", &num[i]);
for(int i=0; i<n; i++) ans[i] = 0;
int x = min(n << 1, m);
solve(x);
x = m / (2 * n);
x--;
for(int i=0; i<n && x>=0; i++)
ans[i] += ans[i] * x;
if(m > n * 2)
solve(m % (n * 2));
for(int i=0; i<n; i++)
{
if(i) printf(" ");
printf("%d", ans[i]);
}
printf("\n");
}
return 0;
}
C. Triangle Pendant
这题队友读完题都乐了,什么模拟物理引擎,反正我是没做
D. Rock Paper Scissors#
石头剪刀布,模拟题
因为后手可以看先手是什么牌,然后再出手,因此先手决策是不重要的
以后手最优,直接模拟就行
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int inf = 2147483647;
const double PI = acos(-1.0);
int num[maxn];
int a[3], b[3];
long long query(int x)
{
long long ans = 0;
int xa = (x + 1) % 3;
int temp = min(a[x], b[xa]);
a[x] -= temp;
b[xa] -= temp;
ans += temp;
temp = min(a[x], b[x]);
a[x] -= temp;
b[x] -= temp;
xa = (x + 2) % 3;
temp = min(a[x], b[xa]);
a[x] -= temp;
b[xa] -= temp;
return ans - temp;
}
signed main()
{
int t;
scanf("%d", &t);
while(t--)
{
for(int i=0; i<3; i++) scanf("%d", &a[i]);
for(int i=0; i<3; i++) scanf("%d", &b[i]);
long long ans = 0;
for(int i=0; i<3; i++)
ans += query(i);
printf("%lld\n", ans);
}
return 0;
}
E. Don't Really Like How The Story Ends#
在给出的一个图中加入最少的边,使得其 dfs 序是从 1 到 n
这题赛中队友说思路的时候一度没听懂,最后队友敲了出来,赛后才补题
直接 dfs 往下搜就行,要保证一下的决策:
-
如果当前结点连接着下一个想要去的结点,则直接访问
-
如果当前结点仍有连接着的结点未访问,而且并不与下一个想要去的结点相邻,则必须要添加一条边
-
如果当前结点的相邻结点已经被访问,且不与下一个想要去的结点相邻,则返回上一个节点
如果每次去遍历那些点没有访问过,会非常消耗时间
直接存边的时候,仅让编号小的点指向编号大的点,然后对每个点发出的边,按指向的点编号从小到大排序,拿一个指针(标记)去指向下一个应该要访问的点即可
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1e5 + 7;
vector<int>gra[maxn];
int pre[maxn], tp = 1, ans = 0;
int s[maxn];
void dfs(int now)
{
if(now == tp)
tp++;
while(s[now] < gra[now].size() && gra[now][s[now]] < tp) s[now]++;
while(s[now] < gra[now].size())
{
int nex = gra[now][s[now]];
if(nex != tp) ans++;
dfs(tp);
while(s[now] < gra[now].size() && gra[now][s[now]] < tp) s[now]++;
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n, m;
scanf("%d%d", &n, &m);
for(int i=0; i<m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
int c = a + b;
a = min(a, b);
b = c - a;
if(a == b) continue;
gra[a].push_back(b);
}
for(int i=1; i<=n; i++) sort(gra[i].begin(), gra[i].end());
ans = 0;
tp = 1;
while(tp <= n)
{
dfs(tp);
ans++;
}
ans--;
printf("%d\n", ans);
for(int i=1; i<=n; i++)
{
gra[i].clear();
s[i] = 0;
}
}
return 0;
}
F. Direction Setting
是一个网络流的问题,我还没学完,咱队也没找到网络流的思路
这学期算法课有网络流,就趁机把它学懂
G. Hourly Coding Problem
听说是个二分
H. Nihongo wa Muzukashii Desu#
模拟题
队友写的,他说没有补题的必要,下面给出队友的代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
const int inf = 2147483647;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--)
{
string s;
cin >> s;
string ans;
if(s == "ikimasu") ans = "itte";
else
{
string cur = s.substr(s.length() - 6);
if(cur == "mimasu" || cur == "bimasu" || cur == "nimasu")
{
ans = s.substr(0, s.length() - 6) + "nde";
}
else if(cur == "kimasu")
{
ans = s.substr(0, s.length() - 6) + "ite";
}
else if(cur == "gimasu")
{
ans = s.substr(0, s.length() - 6) + "ide";
}
else if(cur == "rimasu")
{
ans = s.substr(0, s.length() - 6) + "tte";
}
else if(s.length() > 7)
{
cur = s.substr(s.length() - 7);
if(cur == "chimasu")
{
ans = s.substr(0, s.length() - 7) + "tte";
}
else if(cur == "shimasu")
{
ans = s.substr(0, s.length() - 7) + "shite";
}
else
{
ans = s.substr(0, s.length() - 5) + "tte";
}
}
else
{
ans = s.substr(0, s.length() - 5) + "tte";
}
}
cout << ans << endl;
}
return 0;
}
I. Monster Hunter#
二分答案,然后用模拟贪心的方法去判断是否可行
具体的贪心方案可以看看官方题解
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll maxn = 1e5 + 10;
int n, m;
ll a[maxn], b[maxn];
ll num[10], c[maxn];
bool judge(ll x)
{
ll t = x / n;
for(int i=0; i<=3; i++) num[i] = 0;
for(int i=0; i<n; i++) num[a[i]]++;
for(int i=1; i<=3; i++) num[i] *= t;
t = x % n;
for(int i=0; i<t; i++) num[a[i]]++;
for(int i=0; i<m; i++) c[i] = b[i];
for(int i=0; i<m && num[3]; i++)
{
if(c[i] < 3) continue;
if(c[i] % 2 == 1) c[i] -= 3, num[3]--;
}
for(int i=0; i<m && num[3]; i++)
{
t = min(c[i] / 6, num[3] / 2);
c[i] -= t * 6ll;
num[3] -= t * 2ll;
}
ll maxx = 0, way = 0;
for(int i=0; i<m; i++) if(maxx < c[i]) {maxx = c[i]; way = i;}
if(maxx > 4 && num[3]) {c[way] -= 3; num[3]--;}
for(int i=0; i<m && num[3]; i++) if(c[i] == 4) c[i] -= 3, num[3]--;
for(int i=0; i<m && num[3]; i++) if(c[i] == 2) c[i] = 0, num[3]--;
for(int i=0; i<m && num[3]; i++) if(c[i] == 1) c[i] = 0, num[3]--;
for(int i=0; i<m && num[2]; i++)
{
t = min(c[i] / 2, num[2]);
c[i] -= t * 2ll;
num[2] -= t;
}
for(int i=0; i<m && num[1]; i++)
{
t = min(c[i], num[1]);
c[i] -= t;
num[1] -= t;
}
for(int i=0; i<m && num[2]; i++)
{
t = min((c[i] + 1) / 2, num[2]);
num[2] -= t;
c[i] -= t * 2ll;
}
for(int i=0; i<m; i++) if(c[i] > 0) return false;
return true;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
ll l = 0, r = 0;
scanf("%d", &n);
for(int i=0; i<n; i++) scanf("%lld", &a[i]);
scanf("%d", &m);
for(int i=0; i<m; i++)
{
scanf("%lld", &b[i]);
r += b[i];
}
while(l < r)
{
ll mid = l + r >> 1;
if(judge(mid))
r = mid;
else
l = mid + 1;
}
printf("%lld\n", l);
}
return 0;
}
J. Ants#
一个思维题 + 一点模拟
我们需要明确的是,蚂蚁之间的碰撞是不重要的,我们在乎的只是蚂蚁与边界的碰撞,蚂蚁本身碰撞后,本该出现蚂蚁的位置仍有蚂蚁,只是哪一只蚂蚁不同了而已
所以对于夸张的数据范围,我们只需要考虑最后一点点的时间
对于一开始,n 只蚂蚁在走完 2L 的距离后,都会对两边碰撞一次,所以在 2L 的时间内,两边边界各减少 n 的耐久
到最后再来一次 2L 的时间,边界就要破碎的时候我们就可以自己进行模拟,用两个队列来对蚂蚁的行为进行模拟
按照往左边走和往右边走 来分队列,然后队列内也是按照碰到边界的先后来排列,队头的先碰边界,如果碰到边界会返回,则加入到另外一个队列中去,否则直接就出去,最后计算一个最大值就行了
写在最后,这个想法是我和队友一起讨论出来的,然后队友去敲了,敲了快1个小时都没有弄出来,然后我一看,很多逻辑都有问题,最后我把这题抢过来了
还好只是模拟赛,如果是正式赛,我想我都没办法放心把键盘交出去(五一的集训看下来,我好像每场比赛都是主代码手)
真的要好好好好督促队友刷题,感觉我队友已经很久没有训练了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int inf = 2147483647;
const double PI = acos(-1.0);
ll arr[maxn], brr[maxn];
ll len = 1e9 + 1;
queue<ll> q2; // 右
queue<ll> q1; // 左
signed main()
{
int n, a, b;
scanf("%d %d %d", &n, &a, &b);
for (int i = 1; i <= n; i++)
scanf("%lld", &arr[i]);
for (int i = 1; i <= n; i++)
scanf("%lld", &brr[i]);
ll pre = 0, tr = min(a, b) / n, ans = 0;
pre = tr * len * 2;
tr *= n;
a -= tr, b -= tr;
for(int i=1; i<=n; i++)
if(brr[i] == 0)
q1.push(arr[i]);
for(int i=n; i>=1; i--)
if(brr[i])
q2.push(len - arr[i]);
while(q1.size() || q2.size())
{
while(q1.size())
{
ll now = q1.front();
q1.pop();
if(a)
{
a--;
q2.push(now + len);
}
else
ans = max(ans, pre + now);
}
while(q2.size())
{
ll now = q2.front();
q2.pop();
if(b)
{
b--;
q1.push(now + len);
}
else
ans = max(ans, pre + now);
}
}
printf("%lld\n", ans);
return 0;
}
K. K-skip Permutation#
这题就直接贪心的,按模k同余分组,然后一个个放上去就行了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int inf = 2147483647;
const double PI = acos(-1.0);
int num[maxn];
signed main()
{
int n, k, tp = 0;
cin >> n >> k;
for(int i=1; i<=k; i++)
{
for(int j=i; j<=n; j+=k)
{
num[++tp] = j;
}
}
for(int i=1; i<=n; i++)
{
if(i != 1) cout << " ";
cout << num[i];
}
cout << endl;
return 0;
}
L. Spicy Restaurant#
这题直接按照权值分类,跑 100 次bfs就行
将题目给出的终点作为起点,将所有其他所有点作为终点,多起点的bfs,复杂度还是没有变化,都是
最后的答案就按题目的要求,在符合条件的权值下找到最小值就行了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
const int inf = 2147483647;
const double PI = acos(-1.0);
#define pii pair<int, int>
vector<int>gra[maxn];
vector<int>val[110];
int ans[110][maxn];
int vis[maxn];
void bfs(int n)
{
queue<pair<int, int>>q;
for(int i=0; i<val[n].size(); i++)
{
vis[val[n][i]] = 1;
q.push(make_pair(val[n][i], 0));
}
while(q.size())
{
pii x = q.front();
q.pop();
int now = x.first;
ans[n][now] = x.second;
for(int i=0; i<gra[now].size(); i++)
{
int nex = gra[now][i];
if(vis[nex]) continue;
vis[nex] = 1;
q.push(make_pair(nex, x.second + 1));
}
}
}
signed main()
{
int n, m, q, w = 0;
scanf("%d%d%d", &n, &m, &q);
for(int i=1; i<=n; i++)
{
int a;
scanf("%d", &a);
w = a > w ? a : w;
val[a].push_back(i);
}
for(int i=0; i<m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
gra[a].push_back(b);
gra[b].push_back(a);
}
for(int i=0; i<=w; i++) for(int j=0; j<=n; j++) ans[i][j] = n;
for(int i=1; i<=w; i++)
{
for(int j=0; j<=n; j++) vis[j] = 0;
bfs(i);
}
while(q--)
{
int a, b;
scanf("%d%d", &a, &b);
int x = n;
for(int i=1; i<=b; i++) x = ans[i][a] < x ? ans[i][a] : x;
printf("%d\n", x == n ? -1 : x);
}
return 0;
}
M. True Story#
题目给出了很多对数据范围的限制,所以就变成了一个纯纯思维题
直接找延迟时间里面的最大时间,然后判断够不够乘客到机场就行
如果怕 double 类型会导致精度问题的话,记得开longlong
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 1e5 + 7;
long long num[maxn];
int l[maxn], r[maxn];
int main()
{
int n, m, s, p;
scanf("%d%d%d%d", &n, &m, &s, &p);
for(int i=0; i<n; i++) scanf("%lld", &num[i]);
for(int i=0; i<m; i++) scanf("%d", &l[i]);
for(int i=0; i<m; i++) scanf("%d", &r[i]);
for(int i=0; i<m; i++) p = max(p, r[i] - l[i]);
int ans = 0;
for(int i=0; i<n; i++)
if(num[i] * p >= s) ans++;
printf("%d\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具