Codeforces Round #804 (Div. 2)
2022/7/5 23:27:14
本文主要是介绍Codeforces Round #804 (Div. 2),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Codeforces Round #804 (Div. 2)
这场题感觉都挺有意思的。
A
题意
找到一组解 \((a,b,c)\) 使得 \((a \oplus b) + (b \oplus c) + (a \oplus c) = n\)
没有输出 -1
思路
先看偶数,很容易看出 \((n/2,n/2,0)\) 是合法解
对于奇数。考虑 \(n=1\) 。无解,这是可以枚举的。这等于说最低位上的 \(1\) 是无法构造合法解的,也就是奇数无解。
给出官方题解上的一个证明,用到了加法和异或的关系。
我们知道 \(a+b = a \oplus b + 2 * (a \& b)\) 。通过它可以看出 \(a+b\) 和 \(a \oplus b\) 具有相同的奇偶性。只看奇偶性的话 \((a \oplus b) + (b \oplus c) + (a \oplus c)\) 等价于 $ a+b+b+c+a+c$ 。因此 \(n\) 一定是偶数。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<set> #include<queue> #include<map> #include<stack> #include<string> #include<random> #include<iomanip> #define yes puts("yes"); #define inf 0x3f3f3f3f #define ll long long #define linf 0x3f3f3f3f3f3f3f3f #define ull unsigned long long #define endl '\n' #define int long long #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) using namespace std; mt19937 mrand(random_device{}()); int rnd(int x) { return mrand() % x;} typedef pair<int,int> PII; const int MAXN =10 + 2e5 ,mod=1e9 + 7; void solve() { int n; cin >> n; if(n & 1) { cout << -1 << endl; return ; } cout << n / 2 << " " << n / 2 << " " << 0 << endl; } signed main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); int T;cin>>T; while(T--) solve(); return 0; }
B
题意
给 \(n *m\) 的网格染色,使得每一个格子有且仅有 \(2\) 个邻居和它不同色
思路
构造题,就挺看眼缘的。。。我们可以先构造出 \(2 * m\) 的情况,接下来每行翻转复制即可。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<set> #include<queue> #include<map> #include<stack> #include<string> #include<random> #include<iomanip> #define yes puts("yes"); #define inf 0x3f3f3f3f #define ll long long #define linf 0x3f3f3f3f3f3f3f3f #define ull unsigned long long #define endl '\n' #define int long long #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) using namespace std; mt19937 mrand(random_device{}()); int rnd(int x) { return mrand() % x;} typedef pair<int,int> PII; const int MAXN =10 + 2e5 ,mod=1e9 + 7; void solve() { int n,m; cin >> n >> m; int a[2][3][3]; rep(i,1,2) rep(j,1,2) { if(i == j) a[0][i][j] = 1; else a[0][i][j] = 0; } rep(i,1,2) rep(j,1,2) { if(i == j) a[1][i][j] = 0; else a[1][i][j] = 1; } vector<vector<int>> ans(n + 1,vector<int>(m + 1)); int op = 0; for(int i = 1;i <= 1;i += 2){ for(int j = 1;j <= m;j += 2) { for(int k = 0;k < 2;k ++) for(int p = 0;p < 2;p ++) ans[i + k][j + p] = a[op][k + 1][p + 1]; op ^= 1; } } for(int i = 1;i <= n;i += 2) { if((i + 1) / 2 % 2 == 0) { rep(j,1,2) rep(k,1,m) cout << ans[j][k] << " \n"[k == m]; }else { per(j,2,1) rep(k,1,m) cout << ans[j][k] << " \n"[k == m]; } } } signed main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); int T;cin>>T; while(T--) solve(); return 0; }
C
题意
给一个排列 \(p\) ,求满足下列条件的排列 \(q\) 的方案数。
- \(q,p\) 的任意子段的 \(mex\) 相等。
思路
赛后看大佬一个比一个短的代码有点怀疑人生(。
先分析最小子段,有一个显然的观察,\(0\) 的位置一定不会变。
那么我们试着从这个不动点从小到大考虑。考虑一段区间覆盖 \([0,i]\) ,我们求此区间的 \(mex\) 我们发现,所有比这个 \(mex\) 小的元素不能离开这个区间,否则就会改变 \(mex\) 。我们考虑此时区间中还未安排的位置为 \(sz\) ,比 \(mex\) 小的元素为 \(cnt\) 个。安排它们对答案的贡献就是 \(A_{sz}^{cnt}\) 。需要注意的是如果遇到比 \(mex\) 大的元素要先将其存起来,当之后扩展到新的区间再继续考虑它们。
之后继续扩展区间即可求得答案。
在实现方面可以开两个 \(set\) 。一个放已被使用的元素,一个放比 \(mex\) 大的元素。因为每个集合中的元素最多被插入一次,删除一次,且每个元素在扩展区间时仅会被访问一次,时间复杂度是 \(O(nlogn)\) 的。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<set> #include<queue> #include<map> #include<stack> #include<string> #include<random> #include<iomanip> #define yes puts("yes"); #define inf 0x3f3f3f3f #define ll long long #define linf 0x3f3f3f3f3f3f3f3f #define ull unsigned long long #define endl '\n' #define int long long #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) using namespace std; mt19937 mrand(random_device{}()); int rnd(int x) { return mrand() % x;} typedef pair<int,int> PII; const int MAXN =10 + 2e5 ,mod=1e9 + 7; int fac[MAXN],invfac[MAXN],inv[MAXN]; void init() { inv[1] = invfac[1] = invfac[0] = fac[1] = fac[0] = 1; for(int i = 2;i < MAXN;i ++) { inv[i] = (mod - mod / i) * inv[mod % i] % mod; fac[i] = fac[i - 1] * i % mod; invfac[i] = invfac[i - 1] * inv[i] % mod; } } int C(int n,int m) { return fac[n] * invfac[m] % mod * invfac[n - m] % mod; } int A(int n,int m) { return fac[n] * invfac[n - m] % mod; } void solve() { int n; cin >> n; vector<int> a(n); rep(i,0,n - 1) cin >> a[i]; vector<int> id(n); rep(i,0,n - 1) id[a[i]] = i; int l = id[0],r = id[0]; int mex = 0; vector<bool> st(n); set<int> s, big; s.insert(a[0]); st[0] = 1; int ans = 1; int sz = 0; rep(i,1,n - 1) { int t = id[i]; if(t < l){ // [l,r] -> [t,r] [t,l - 1] rep(j,t,l - 1) { sz ++; int x = a[j]; st[a[j]] = 1; while(st[mex]) mex ++; } int cnt = 0; s.insert(a[t]); sz --; rep(j,t,l - 1) { if(a[j] < mex && s.count(a[j]) == 0) cnt ++, s.insert(a[j]); else if(s.count(a[j]) == 0 && a[j] > mex) big.insert(a[j]), s.insert(a[j]); } vector<int> v; for(auto x : big) { if(x > mex) break; if(x < mex) { cnt ++; v.push_back(x); } } for(auto x : v) big.erase(x); ans = (ans * A(sz,cnt)) % mod; sz -= cnt; l = t; } else if(t > r){ rep(j,r + 1,t) { sz ++; st[a[j]] = 1; while(st[mex]) mex ++; } int cnt = 0; s.insert(a[t]); sz --; rep(j,r + 1,t) { if(a[j] < mex && s.count(a[j]) == 0) cnt ++, s.insert(a[j]); else if(s.count(a[j]) == 0 && a[j] > mex) big.insert(a[j]), s.insert(a[j]); } vector<int> v; for(auto x : big) { if(x > mex) break; if(x < mex) { cnt ++; v.push_back(x); } } for(auto x : v) big.erase(x); ans = (ans * A(sz,cnt)) % mod; sz -= cnt; r = t; } } cout << ans << endl; } signed main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); init(); int T;cin>>T; while(T--) solve(); return 0; }
D
题意
给一个序列 \(a\),若 $a_i \neq a_{i + 1} $ 就可以删除它们。现在要求把序列删到所有值都相等,问最后剩下的最长长度。
思路
手玩了一下样例,感觉有点括号序列的意思,找左右括号看是否匹配,然后在歪路上越走越远···
正解应该是考虑dp。因为这是一个子序列删除问题,感觉还是很常见的。
我们定义 \(dp[i]\) 表示考虑前 \(i\) 个元素,且保留第 \(i\) 位元素的保留长度最大值
那么最后答案就是 \(max\{dp[i]\},当[i+1,n]所有元素都可删除时i可取到\) 。
考虑转移,如果前驱状态到现态中间所有元素都可以删除,那么可以有 \(dp[i] = dp[j]+1\) 的转移。
现在就需要考虑这样一个子问题:什么时候可以将一段区间中所有元素删除。
结论是当出现次数最多的元素不多于 \(\frac{len}{2}\) 且 \(len\) 为偶数时可删除。
给出如下证明:
对必要性,显然
对充分性。我们设当前出现最多数字的数量为 \(c\) ,当前长度为 \(len\) 。\((c\le len/2)\)
引入一个结论:当 \(c < len\)时, 出现最多的元素总可以找到一个和它不同的元素进行删除。(反证法)
那么我们就可以有如下的删除策略。
- 删除出现最多的元素和一个其他元素
- 检查新序列最大元素,删除它和其他元素
- 重复12直到无法删除
在第2步中,查出的新的最多元素数量 \(x < c < len\) 根据引入结论,删除总是可行的。
预处理出可以可全删除的区间,之后正常做dp即可。另外需要注意的是dp数组初始化,对于 \([1,i-1]\) 全部可删,初始为 \(1\)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<set> #include<queue> #include<map> #include<stack> #include<string> #include<random> #include<iomanip> #define yes puts("yes"); #define inf 0x3f3f3f3f #define ll long long #define linf 0x3f3f3f3f3f3f3f3f #define ull unsigned long long #define endl '\n' #define int long long #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) using namespace std; mt19937 mrand(random_device{}()); int rnd(int x) { return mrand() % x;} typedef pair<int,int> PII; const int MAXN =10 + 2e5 ,mod=1e9 + 7; void solve() { int n; cin >> n; vector<int> a(n + 1); rep(i,1,n) cin >> a[i]; int ans = 0; vector<vector<int>> f(n + 1,vector<int>(n + 1)); rep(i,1,n) { vector<int> cnt(n + 1); int mx = 0; rep(j,i,n) { mx = max(mx , ++ cnt[a[j]]); if((j - i + 1) % 2 == 0 && 2 * mx <= j - i + 1) f[i][j] = 1; } } vector<int> dp(n + 1,-linf); // dp[i] = max dp[j] + 1 dp[0] ? dp[0] = 0; dp[1] = 1; rep(i,2,n) if(f[1][i - 1]) dp[i] = 1; rep(i,1,n) { rep(j,1,i - 1) { if(a[i] == a[j] && (f[j + 1][i - 1] || j + 1 > i - 1)){ dp[i] = max(dp[i], dp[j] + 1); } } } rep(i,1,n - 1) if(f[i + 1][n]) ans = max(ans, dp[i]); cout << max(ans, dp[n]) << endl; } signed main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); int T;cin>>T; while(T--) solve(); return 0; }
这篇关于Codeforces Round #804 (Div. 2)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享