Codeforces Round 970 div3
错过的一场比赛,第二天早晨VP,题目的通过率还挺奇怪
A. Sakurako's Exam
https://codeforces.com/contest/2008/problem/A
题意:有a个1和b个2的数组,在1和2前面增添加减号,判断是否能让结果为0
思路:1个2需要2个1进行填补,不如先让2自己进行消去,如果剩一个2再判断是否能拿两个1进行消去,之后看剩余的1是否能自己消去
void solve() {
int a,b;
std::cin >> a >> b;
b %= 2;
if(b != 0 && a >= 2 * b) {
a -= 2 * b;
b = 0;
}
if(a % 2 == 0 && !b) std::cout << "YES" << "\n";
else std::cout << "NO\n";
}
B. Square or Not
https://codeforces.com/contest/2008/problem/B
题意:对于一个给定的01串,判断元素受否能依次填充到一个正方形矩阵中同时矩阵的边缘为1内部为0(这里用的手动开根,防止精度 问题)
思路:先判断串的长度是否是完全平方数再直接暴力模拟
int rsqrt(int x) {
int l = 0,r = 500;
while(l < r) {
int mid = (l + r + 1) >> 1;
if(mid * mid <= x) l = mid;
else r = mid - 1;
}
return l;
}
void solve() {
int n;
char a[N][N];
std::string str;
std::cin >> n;
std::cin >> str;
if(rsqrt(n) * rsqrt(n) != n) std::cout << "No\n";
else {
int t = rsqrt(n);
for(int i = 1;i <= t;i ++ )
for(int j = 1;j <= t;j ++ ) {
a[i][j] = str[(i - 1) * t + j - 1];
}
bool ok = true;
for(int i = 1;i <= t;i ++ )
for(int j = 1;j <= t;j ++ ) {
if((i == 1 || i == t || j == 1 || j == t) && a[i][j] == '0') ok = false;
if(!((i == 1 || i == t || j == 1 || j == t)) && a[i][j] == '1') ok = false;
}
std::cout << (ok ? "Yes" : "No") << "\n";
}
}
C. Longest Good Array
https://codeforces.com/contest/2008/problem/C
题意:好数组的定义:数组a是单调的并且相邻元素的差值也是单调的,即 对于 $ 2 \leq i \leq n $ 有 $ a_{i - 1} < a_{i} $ 同时对于 $ 2 \leq i < n $ 有 $ a_i - a_{i - 1} < a_{i + 1} - a_i $ ,给定边界 $ l $ $ r $ ,找到处于区间中的好数 组的最大长度
思路:很容易想到每次相邻元素的差值是从1慢慢增加可以让好数组的增长速度最小,即长度最大化
列出表达式 $ a_i = a_1 + \sum\limits_{j = 1}^{i - 1}j$ ,因此很容易知道长度是i可以取到的最大值,这里具有明显的单调性,可以直接二分解决
void solve() {
std::cin >> x >> y;
int l = 0,r = 44722;
while(l < r) {
int mid = (l + r + 1) >> 1;
if(x + mid * (mid + 1) / 2 <= y) l = mid;
else r = mid - 1;
}
std::cout << l + 1 << "\n";
}
D. Sakurako's Hobby
https://codeforces.com/contest/2008/problem/D
题意:给定一个排列P,有 \(i\) 可以到达 \(p_i\) ,同时给定01串A,有 \(A_i = '0'\) ,i为黑色,反之为白色,求每个点可以到达的黑色点的个数
思路:看到 \(i = P_i\) 想到置换环,同时对于一个环中的下标,可以到达的黑色的点的个数一定的相同的(类似缩点?),因此我们考虑用并查集来维护
int find(int x) {
return (f[x] == x ? f[x] : find(f[x]));
}
void bfs(int x) {
std::queue<int> q;
ok[x] = true;
q.push(x);
while(!q.empty()) {
int t = q.front();
q.pop();
for(auto i : e[t]) {
if(ok[i]) continue;
ok[i] = true;
f[find(i)] = find(t);
if(a[i] == '0') size[find(t)] += 1;
q.push(i);
}
}
}
void init() {
for(int i = 0;i <= n;i ++ ) {
e[i].clear();
f[i] = i;
ok[i] = false;
}
}
void solve() {
std::cin >> n;
init();
for(int i = 1;i <= n;i ++ ) {
int x;
std::cin >> x;
e[i].push_back(x);
}
std::cin >> a;
a = " " + a;
for(int i = 1;i <= n;i ++ ) {
size[i] = 0;
if(a[i] == '0') size[i] = 1;
}
for(int i = 1;i <= n;i ++ ) {
if(!ok[i]) bfs(i);
}
for(int i = 1;i <= n;i ++ )
std::cout << size[find(i)] << " \n"[i == n];
}
F. Sakurako's Box
https://codeforces.com/contest/2008/problem/F
题意:计算盒子中任意选择两个球价值相乘的期望
思路:即计算盒子所有可能的球价值乘积再求和,最后除总的情况数,看似是 $ O(n^2) $
我们列表达式 \(\sum\limits_{i=1}^n(\sum\limits_{j=i}^n{a_i \times a_j})\) 化简为
\(\sum\limits_{i=1}^n(a_i \times \sum\limits_{j=i}^na_j)\) 发现可以使用前缀和维护,同时总情况数通过 \(C^2_n\) 来计算,同时求逆元
启发:看到乘积混合加法时,可以考虑同类项合并后用前缀和维护!!!
类似题目:https://codeforces.com/problemset/problem/1996/E
int quickPow(int a,int b) {
int res = 1;
while(b) {
if(b & 1) res = res * a % Mod;
a = a * a % Mod;
b >>= 1;
}
return res;
}
void init() {
fact[0] = 1,infact[0] = 1;
s[0] = 0,a[0] = 0;
for(int i = 1;i <= n;i ++ ) {
s[i] = 0;
a[i] = 0;
fact[i] = (fact[i - 1] % Mod * (i % Mod)) % Mod;
infact[i] = quickPow(fact[i],Mod - 2);
}
}
int C(int n,int m) {
return ((fact[n] * infact[n - m]) % Mod * infact[m]) % Mod;
}
void solve() {
std::cin >> n;
init();
for(int i = 1;i <= n;i ++ ) {
std::cin >> a[i];
s[i] = (s[i - 1] + a[i] % Mod) % Mod;
}
int P = 0;
for(int i = 1;i <= n;i ++ ) {
int x = (s[n] - s[i] + Mod) % Mod;
P = ( P + a[i] % Mod * x ) % Mod;
}
int Q = quickPow(C(n,2),Mod - 2);
int ans = (P * Q) % Mod;
std::cout << ans << "\n";
}
标签:std,970,int,com,codeforces,Codeforces,https,problem,div3
From: https://www.cnblogs.com/LZzzJ/p/18393604