这场又又又被打爆了,但赛时状态比上一场好些,可能是因为正在磕的题有切入点导致的qwq。
比较难绷的是,本场的签到题没做出来。
B. Memory
还是思维太差劲了,应该想到整数和小数部分可以分开处理,具体细节见代码。
#include <bits/stdc++.h>
//#define int long long
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define fr first
#define se second
#define endl '\n'
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;const int maxn = 2e5 + 5;void solve()
{int n;cin >> n;vector<int> a(n + 1);string ans = "";ll sum_int = 0;bool hve_dot = false;for(int i = 1; i <= n; i ++){cin >> a[i];if(sum_int % 2){hve_dot = true;}if(sum_int < 0) sum_int = (sum_int - 1) / 2 + a[i]; // !!注意负数时的向下取整else sum_int = sum_int / 2 + a[i];if(sum_int > 0) ans.pb('+');else if(sum_int == 0){if(hve_dot) ans.pb('+');else ans.pb('0');}else ans.pb('-');}cout << ans << endl;
}// 10
// 2 -1 4 -7 4 -8 3 -6 4 -7signed main()
{ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);// int T=1; cin>>T; while(T--)solve();return 0;
}
只做出了 \(L,M\) 两道签到呜呜呜...
G. The Only Way to the Destination
很容易想到将整张图看作一棵树,其中空格子代表树的结点,相邻的两个空格子可看作一条边。由于保证整张图的空格连通,所以只要整张图中存在环,就必然不合法。合法当且仅当边数 = 点数 - 1,计算图中的点数和边数即可。
点数可以直接求;边数需要根据图的特殊性,将整张图分成 \(O(m + k)\) 个空格连续段。而当 \(min(n,m)>1\) 且 \(k < \lfloor \frac{m}{2} \rfloor\) 时,必然存在两个相邻的全空格连续段,那么整张图就必然存在一个 \(2 \times 2\) 的空格子图,一定不合法;就此可以利用这个性质将 \(m\) 降至 \(O(k)\) 级别,边数的计算复杂度也会降到 \(O(k)\),具体细节见代码。当然也可以不用 边数=点数-1 这个结论,直接用并查集,在合并前若两个点已经在同一集合就说明存在环,不合法。
code
\(upd:\) 蒟蒻第一发代码的思路是完全正确的,就差了一行代码:在双指针 \(i\)-- 的位置,需要注意初始 \(i = 0\) 时可能会将 \(i\) 减到负数,从而导致后续判断中数组出现负的下标(那为毛不报RE呢。。。),因此需要在后面补充一句 \(i = max(0, i)\)。看来最近没太写双指针的题,对边界的判断有些生疏了。
呜呜呜太难绷了,比赛就是这样残酷。
J. Game on a Forest
\(SG\) 函数打表题。可以将整个游戏分成多个相互独立的子游戏,每个子游戏的 \(sg\) 值异或和就是整个游戏的 \(sg\) 值。打表或手玩可发现每个子游戏先手是否必胜只与子树大小有关:奇数大小的树 \(sg\) 值为 \(1\);偶数大小的树 \(sg\) 值为 \(2\)。第一步操作最多只有 \(n + m\) 种,只需要提前预处理每棵树的子树信息即可做到 \(O(1)\) 查询操作后整个游戏的 \(sg\) 值,若为 \(0\) 则累计答案。
code
D. A Simple MST Problem
一道我感觉特别不好做的思维题,补了我一整天。
最暴力的想法当然是将所有边处理出来,然后跑最小生成树,然而边数是 \(O(n^{2})\) 的,显然不能枚举所有边,需要利用 \(w(x)\) 的一些性质来优化。
注意到对于点 \(x\) 引出的某一条边,边权必然是 \(\geq w(x)\) 的;而若整个区间内包含某个质数(或者说质因子种类数只有一种),那么我们将其他点均与这个质数连边,边权必然是 \(\leq w(x) + 1\) 的。因此对于任意一条边,考虑两个端点中最大的 \(w(x)\),边权只有 \(w(x)\) 与 \(w(x) + 1\) 两种取值。那么我们只需要思考最大化 \(w(x) + 1\) 到 \(w(x)\) 的优化就可以了。
当点 \(x\) 引出的某条边的边权为 \(w(x)\) 时,等价于:该边的另一个端点 \(y\) 的质因子集合一定被 \(x\) 所包含。因此要最大化上述优化,我们只需要每次贪心地先将相互包含质因子集合的两个点连边就可以了。每个数的质因子集合是可以预处理的,而这种连边也可以利用类似埃氏筛的写法优化:
vector<int> st(r + 1, false);for(auto x : S){if(st[x]){ans += (int)pfac[x].size();}else{if(x == P) ans ++;else{ans += (int)pfac[x].size() + (int)pfac[P].size() - (int)pfac[__gcd(x, P)].size();for(int j = x; j <= r; j += x){st[j] = true;}}}}
这样可以保证每个数只会被最小的被包含的质因子集合筛掉,因此每个数只会被筛一次,复杂度是 \(O(n)\) 的。
对于区间中不包含质数的情况,由质数分布可知,这样的区间不会很长,直接暴力跑最小生成树即可。
code