官方题解 | 第三届飞翔杯普及组
2025-06-14 17:05:19
发布于:浙江
T1 考试(exam)
模拟判断两条原则是否满足,首先判断第一个原则:是否有连续的两个 ,其次判断第二个原则:对于每一个为 的位置,判断其前后是否没有 ,如果都不是 则可以将这个 变为 ,输出 No
。
注意边界的情况,如果前面的三个位置是 时不满足第二个条件,同理最后三个位置是 时也不满足条件。
#include<bits/stdc++.h>
using namespace std;
char s[100005];
int main()
{
int n,q;
freopen("exam.in","r",stdin);
freopen("exam.out","w",stdout);
scanf("%d%d",&n,&q);
while(q--)
{
int opt=0;
scanf("%s",s+1);
for(int i=1;i<=n;i++)
{
if(s[i]=='1'&&s[i+1]=='1')opt=1;
else if(s[i]=='0'&&s[i-1]!='1'&&s[i+1]!='1')opt=1;
}
if(!opt)printf("Yes\n");
else printf("No\n");
}
return 0;
}
T2:好数(number)
40%
枚举即可。
70%
维护小于的的和即可,时间复杂度
100%
通过类似背包的方式维护,维护两个数字构成的和的情况,三个数字构成和的情况,用bitset
优化加速即可,时间复杂度
100%
表达式类问题可以通过移项优化枚举策略
枚举和,维护小于的的和即可。
复杂度
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5005;
int a[maxn], n, ans;
unordered_set<int> s;
int main() {
freopen("number.in", "r", stdin);
freopen("number.out", "w", stdout);
cin>>n;
for(int i = 1; i <= n; ++i) cin>>a[i];
for(int i = 1; i <= n; ++i) {
bool flag = false;
for(int j = 1; j < i; ++j) {
if(s.find(a[i] - a[j]) != s.end()) flag = true;
}
ans += flag;
for(int j = 1; j <= i; ++j) s.insert(a[i] + a[j]);
}
cout<<ans;
return 0;
}
T3 小 Z 的手套(gloves)
20分做法
,将左手手套和右手手套排序后,首位逐一匹配即可。
另外50分
发现瓶颈在于匹配,匹配的前提是不会超过答案,因此可以考虑从小到大枚举丑陋度(即尺码差)的最大值,然后对于每一个左手手套贪心匹配右手手套,这个可以通过排序和二分来加速。
同时发现丑陋度(即尺码差)的最大值一定是左手和右手手套尺码组合出来的值,可能的答案不超过 种,复杂度 。
100分做法
最小化最大值,考虑二分答案。
首先,我们需要确定丑陋度的可能范围。最小丑陋度显然是 (如果恰好有匹配的尺码),最大丑陋度则是左手手套和右手手套中的最大差值。我们可以使用二分答案来在这个范围内搜索最优解。
在二分查找的每一步中,我们假设一个丑陋度阈值 mid
,然后尝试找到满足这个阈值的最大匹配数。我们可以对左手手套和右手手套的尺码分别进行排序,然后使用双指针的方法从两端开始匹配,如果两只手套的尺码差小于等于mid
,则它们可以配对,然后移动两个指针。最终,我们得到了满足丑陋度阈值mid
的最大匹配数。
如果匹配数等于最大能够配对成功的手套数量(即左手手套和右手手套数量中的最小值),那么mid
可能是一个解,我们尝试缩小搜索范围到左半部分;否则,我们需要增加丑陋度阈值,所以搜索范围移动到右半部分。
复杂度。
「补充证明双指针配对的正准确性」
- 讲左右手套按照大小从小到大排序,然后两个指针从头开始配对,证明 配对的 一定是单调的,并且此时一定是最优匹配。
- ;,假设 与 配对,已知 并且 ,需要证明
- 只有两种情况使得上述情况成立
- 无论是哪种情况,都有
- 只有两种情况使得上述情况成立
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, m, l[maxn], r[maxn];
bool check(int x) {
int j = 1;
for(int i = 1; i <= n; ++i) { // 用数量少的手套匹配多的手套
while(j <= m && abs(l[i] - r[j]) > x) ++j;
if(j > m) return false;
++j;
}
return true;
}
int main() {
freopen("gloves.in", "r", stdin);
freopen("gloves.out", "w", stdout);
cin>>n>>m;
for(int i = 1; i <= n; ++i) cin>>l[i];
for(int i = 1; i <= m; ++i) cin>>r[i];
if(n > m) {
for(int i = 1; i <= n; ++i) swap(l[i], r[i]);
swap(n, m);
}
sort(l + 1, l + 1 + n);
sort(r + 1, r + 1 + m);
int left = 0, right = 1e9, mid, ans;
while(left <= right) {
mid = (left + right) / 2;
if(check(mid)) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
cout<<ans;
return 0;
}
T4 刷题训练(train)
20分做法
对于 的数据,
没有任何操作可以做,所以只有所有的 都满足 才可以,答案是 ;否则是
另外20分
对于另外 的数据,只有第一种类型的魔法(即修改某一道题的难度)
如果所有 的都可以修改,答案就是这样子的题目个数;否则是
另外20分
对于另外 的数据,第一种类型的魔法只有一个。
考虑图论做法,交换两个题,本质上是连边,只要一个点能被换到有第一类操作的地方,那么这个点就可以被修改。
因此找到第一类魔法操作的点,跑一个单源最短路,答案就是所有不合法的点的 dis 之和 + 不合法点的个数。
满分做法
对于全部数据,。
我们可以把所有第一类魔法操作的点放到队列中,跑最短路。
因为图没有边权,所以可以直接用 BFS 求最短路即可,复杂度 。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 2e6 + 5;
int a[maxn], dis[maxn], n, m, k;
bool vis[maxn];
vector<int> g[maxn], v;
queue<int> q;
int main() {
freopen("train.in", "r", stdin);
freopen("train.out", "w", stdout);
ios::sync_with_stdio(false); cin.tie(0);
cin>>n>>m>>k;
for(int i = 1; i <= n; ++i) {
cin>>a[i];
if(a[i] > k) v.push_back(i); // 不合法的点
}
memset(dis, 0x3f, sizeof(dis));
while(m--) {
int op, x, y;
cin>>op;
if(op == 1) {
cin>>x;
dis[x] = 0;
} else {
cin>>x>>y;
g[x].push_back(y); g[y].push_back(x);
}
}
for(int i = 1; i <= n; ++i) {
if(dis[i] == 0) {
vis[i] = true;
q.push(i);
}
}
while(!q.empty()) {
int u = q.front(); q.pop();
for(int v: g[u]) {
if(!vis[v]) {
dis[v] = dis[u] + 1;
q.push(v);
vis[v] = true;
}
}
}
for(int x: v) {
if(dis[x] == inf) {
cout<<-1;
return 0;
}
}
ll ans = v.size();
for(int x: v) ans += dis[x];
cout<<ans;
return 0;
}
全部评论 1
T2疑似折半搜索(
12小时前 来自 广东
0
有帮助,赞一个