5.18
Codeforces Round #689 (Div. 2, based on Zed Code Competition)
B. Find the Spruce
传送门
题意:在矩阵中判断圣诞树的个数。
有点像数字三角形
集合:f[i, j] 点[i, j] 往下所能构成的圣诞树个数。
属性:max
状态方程:dp[i][j] = min(dp[i + 1][j], min(dp[i + 1][j + 1], dp[i + 1][j - 1])) + 1
注意要从下往上推。每次更新总和。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N = 505;
typedef long long ll;
typedef pair<int, int> PII;
int dp[N][N];
int t, n, m;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t -- )
{
memset(dp, 0, sizeof dp);
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
{
char c;
cin >> c;
if (c == '*') dp[i][j] = 1;
}
int ans = 0;
for (int i = n; i >= 1; i -- )
for (int j = 1; j <= m; j ++ )
{
if (dp[i][j])
dp[i][j] = min(dp[i + 1][j], min(dp[i + 1][j + 1], dp[i + 1][j - 1])) + 1;
ans += dp[i][j];
}
cout << ans << endl;
}
return 0;
}
C. Random Events
传送门
算概率
如果前k个数无序的话,我们只用从r>=k的操作中选出一个操作就行了,算出r>=k的操作一个都不执行的概率,1减下就是整个数组有序的概率。
如果整个数组已经有序,也就是pos = 0,直接输出1即可。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 1e5 + 5;
typedef long long ll;
typedef pair<int, int> PII;
int t, n, m;
int a[N];
int main()
{
IOS;
cin >> t;
while (t -- )
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
int pos;
for (pos = n; pos >= 1; pos -- )
{
if (a[pos] != pos) break;
}
double ans = 1.0;
while (m -- )
{
int r;
double p;
cin >> r >> p;
if (r >= pos) ans = ans * (1.0 - p);
}
if (!pos) puts("1");
else printf("%.8lf\n", 1.0 - ans);
}
return 0;
}
D. Divide and Summarize
传送门
这题的答案与数组是否有序无关,因此可以先排个序,可能的情况最多有logn种,因此将所有可能的情况存储在集合中即可,输入一个总和判断集合中是否有这个数。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 1e5 + 5;
typedef long long ll;
typedef pair<int, int> PII;
int t;
int n, q;
ll a[N], pre[N];
set<ll> s;
void dfs(int l, int r)
{
if (l > r) return;
s.insert(pre[r] - pre[l - 1]);
int mid = a[l] + a[r] >> 1;
int pos;
for (pos = l; pos <= r; pos ++ )
if (a[pos] > mid) break;
if (pos == l) return;
if (pos > r) return;
dfs(l, pos - 1);
dfs(pos, r);
}
int main()
{
IOS;
cin >> t;
while (t -- )
{
s.clear();
cin >> n >> q;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i ++ )
pre[i] = pre[i - 1] + a[i];
dfs(1, n);
while (q -- )
{
int sum;
cin >> sum;
if (s.count(sum)) puts("YES");
else puts("NO");
}
}
return 0;
}
5.18
Educational Codeforces Round 109 (Rated for Div. 2)
A. Potion-making
看到一位学姐写的非常优美的代码。
#include<bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int k;
cin >> k;
int p = 100 - k;
int tmp = __gcd(p, k);
cout << (100 / tmp) << endl;
}
}
B. Permutation Sort
三种情况分类讨论下。
在这里插入代码片#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N = 55;
typedef long long ll;
typedef pair<int, int> PII;
int a[N];
int t, n;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t -- )
{
cin >> n;
bool f = 1;
for (int i = 1; i <= n; i ++ )
{
cin >> a[i];
if (a[i] != i) f = 0;
}
if (f)
{
puts("0");
continue;
}
if (a[1] == 1 || a[n] == n)
{
puts("1");
continue;
}
if (a[1] == n && a[n] == 1)
{
puts("3");
continue;
}
puts("2");
}
return 0;
}
D. Armchairs
传送门
将有人的位子和没人的位子的下标分别放在两个数组中。
dp[i][j]表示将i个人让到j个空的位子中所需要的最小花费,如果总花费要最小,使每个元素都最小即可。
状态方程。两种情况
dp[i][j] = dp[i - 1][j - 1] + abs(a[i] - b[j]).(都取最左边的)
如果i个人能放在j - 1个位子中,则dp[i][j] 与 dp[i][j - 1]取个最小值。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 5050;
typedef long long ll;
typedef pair<int, int> PII;
int n;
int a[N], b[N];
int dp[N][N];
int main()
{
IOS
cin >> n;
int x;
int cnt1 = 0, cnt2 = 0;
for (int i = 1; i <= n; i ++ )
{
cin >> x;
if (x) a[++ cnt1] = i;
else b[++ cnt2] = i;
}
memset(dp, 0x3f, sizeof dp);
for (int i = 0; i <= cnt2; i ++ )
dp[0][i] = 0;
for (int i = 1; i <= cnt1; i ++ )
for (int j = 1; j <= cnt2; j ++ )
{
dp[i][j] = dp[i - 1][j - 1] + abs(a[i] - b[j]);
if (i < j) dp[i][j] = min(dp[i][j], dp[i][j - 1]);
}
cout << dp[cnt1][cnt2] << endl;
return 0;
}
Divide by Zero 2021 and Codeforces Round #714 (Div. 2)B. AND Sequences
传送门
与的性质:一堆数与在一起,大小不超过这堆数中的最小值。所以,数组的头尾必须是最小值,否则随着指针从左往右扫,一定会出现左右不相等的情况。同时,因为题目要求的是相等,这里只是不超过,因此数组的与总和还要等于最小值。因为如果在与的过程中出现了比最小值还要小的数,那么后面的与的值就会小于等于这个数。所以只判断总和即可。
注:这题我用了puts(“0”)出现了奇怪的错误。
然后就用cout了。
呜呜,IO流是不能用puts的,切记切记
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 2e5 + 5;
const int MOD = 1e9 + 7;
typedef long long ll;
typedef pair<int, int> PII;
int t, n;
int a[N];
int main()
{
IOS
cin >> t;
while (t -- )
{
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
ll mi = INF;
ll cnt = 0;
for (int i = 1; i <= n; i ++ )
if (mi > a[i])
{
mi = a[i];
cnt = 1;
}
else if (a[i] == mi) cnt ++;
int temp = a[1];
for (int i = 2; i <= n; i ++ )
temp = temp & a[i];
if (temp != mi || cnt < 2) cout << 0 << endl;
else
{
ll ans = cnt * (cnt - 1) % MOD;
for (int i = n - 2; i > 1; i -- )
ans = ans * i % MOD;
cout << ans << endl;
}
}
return 0;
}
C. Add One
传送门
我们从1可是加,加到数是109时,我们能发现后面的长度,都能用9和10来推出来。
线性dp
dp[i]:10经过i次操作后所达到的长度。
dp[i] = 2; 0<=i<=8
dp[i] = 3, i = 9;
状态方程dp[i] = dp[i - 10] + dp[i - 9] i>9
算出所有的数据,打表。
注:从0~10所需的操作数记得算上。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 2e5 + 5;
const int MOD = 1e9 + 7;
typedef long long ll;
typedef pair<int, int> PII;
int dp[N]; //10操作i次后的长度
void init()
{
for (int i = 0; i <= 8; i ++ ) dp[i] = 2;
dp[9] = 3;
for (int i = 10; i <= N; i ++ )
dp[i] = (dp[i - 9] + dp[i - 10]) % MOD;
}
int main()
{
init();
IOS
int t;
cin >> t;
while (t -- )
{
int n, m;
cin >> n >> m;
int ans = 0;
while (n > 0)
{
int x = n % 10;
if (x + m < 10) ans += 1;
else
{
ans = (ans + dp[m - 10 + x]) % MOD;
}
n /= 10;
}
cout << ans << endl;
}
return 0;
}
5.19
今天有道1700的花了较长时间,只写了两道题。
Codeforces Round #688 (Div. 2)B. Suffix Operations
传送门
差分 对数组的后缀如i后的元素加1或减1操作,只会改变i - 1和i个数的差值,不会改变后面相邻元素的差值。如果题目没有要求可以改变一个数的值,那么答案已经出来了,枚举每个数,找到可以减少操作数最多的数。头尾要特殊判断。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 2e5 + 5;
const int MOD = 1e9 + 7;
typedef long long ll;
typedef pair<int, int> PII;
int t;
int n;
int a[N];
int main()
{
IOS
cin >> t;
while (t -- )
{
cin >> n;
for (int i = 1; i <= n; i ++ )
cin >> a[i];
ll sum = 0;
for (int i = 2; i <= n; i ++ )
sum += abs(a[i] - a[i - 1]);
//数组的头尾特殊处理
int maxx = max(abs(a[1] - a[2]), abs(a[n] - a[n - 1]));
for (int i = 2; i < n; i ++ )
{
maxx = max(maxx, abs(a[i] - a[i - 1]) + abs(a[i] - a[i + 1]) - abs(a[i - 1] - a[i + 1]));
}
cout << sum - maxx << endl;
}
return 0;
}
C. Triangles
传送门
枚举每一个点,八种情况此点到最左端的点,到最右端的点,下端的点,上端的点,因为另外一个点可以自己订,因为面积要最大,点要取在边界处。
注:如果数d能构成三角形,那么d最少需要有两个。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 2005;
const int MOD = 1e9 + 7;
typedef long long ll;
typedef pair<int, int> PII;
int t, n;
int a[N][N];
int ans[15], num[15];
int maxx[15], minx[15];
int maxy[15], miny[15];
void init()
{
for (int i = 0; i < 15; i ++ )
{
ans[i] = 0, num[i] = 0;
maxx[i] = 0, minx[i] = N;
maxy[i] = 0, miny[i] = N;
}
}
int main()
{
// IOS
// 切记切记,IO流不能用scanf和printf,puts之类的也不能用
cin >> t;
while (t -- )
{
init();
cin >> n;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
{
scanf("%1d", &a[i][j]);
int k = a[i][j];
num[k] ++;
maxx[k] = max(maxx[k], i);
minx[k] = min(minx[k], i);
maxy[k] = max(maxy[k], j);
miny[k] = min(miny[k], j);
}
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
{
int k = a[i][j];
if (num[k] < 2) continue;
if (maxx[k] != 0) ans[k] = max(ans[k], abs(i - maxx[k]) * max(n - j, j - 1));
if (minx[k] != N) ans[k] = max(ans[k], abs(i - minx[k]) * max(n - j, j - 1));
if (maxy[k] != 0) ans[k] = max(ans[k], abs(j - maxy[k]) * max(n - i, i - 1));
if (miny[k] != N) ans[k] = max(ans[k], abs(j - miny[k]) * max(n - i, i - 1));
}
for (int i = 0; i <= 9; i ++ )
printf("%d ", ans[i]);
printf("\n");
}
return 0;
}
5.20
Educational Codeforces Round 102 (Rated for Div. 2)C. No More Inversions
传送门
构造题。
醉了,今天就看了这一道,也没看懂是怎么构造的,晚上520cf好运,这题明天再想。
打表找规律,证明过程真看不懂了。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 1005;
const int MOD = 1e9 + 7;
typedef long long ll;
typedef pair<int, int> PII;
int t, n, k;
int main()
{
IOS
cin >> t;
while (t -- )
{
cin >> n >> k;
int d = n - k + 1;
for (int i = 1; i <= k - d; i ++ ) cout << i << ' ';
for (int i = k; i >= k - d + 1; i -- ) cout << i << ' ';
cout << endl;
}
return 0;
}
Codeforces Round #721 (Div. 2)
A. And Then There Were K
比赛的时候写了个暴力找了下规律出来了。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 2005;
const int MOD = 1e9 + 7;
typedef long long ll;
typedef pair<int, int> PII;
int t;
ll n;
int main()
{
IOS
cin >> t;
while (t -- )
{
cin >> n;
int i = 2, cnt = 1;
while (1)
{
if (cnt >= n) break;
cnt *= i;
}
if (cnt > n) cnt /= 2;
cout << cnt - 1 << endl;
}
return 0;
}
B1. Palindrome Game (easy version)
太背了,比赛的时候一直没想到标解,试了好多次。
按0的个数为偶数或奇数来讨论。
0为偶数时,A先填头部的一个0,B再填尾部和他对应的一个0,再0还剩下一个的时候,B执行反转操作,而A只能被迫填剩下的0,所以BOB肯定赢。
0为奇数时,1个BOB赢,大于等于3时,A先填中间的0,这样A就拥有了一个后手的优势,前面偶数的情况证明了后手能产生2的优势。所以最后A能以1的优势胜出。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 1005;
const int MOD = 1e9 + 7;
typedef long long ll;
typedef pair<int, int> PII;
int t, n;
char s[N];
int main()
{
IOS
cin >> t;
while (t -- )
{
cin >> n;
cin >> s;
int cnt = 0;
for (int i = 0; i < n; i ++ )
if (s[i] == '0') cnt ++;
if (cnt == 1) cout << "BOB" << endl;
else if (cnt % 2 == 1) cout << "ALICE" << endl;
else cout << "BOB" << endl;
}
return 0;
}
B2. Palindrome Game (hard version)
就是加倍阴间的分类讨论。
将无法构成回文串的个数用dir统计下。当dir为0时,就是第一种情况。
dir为3时,此时A再前3次操作中都采取翻转操作,那么A就拥有了3个优势,就算B抢到了后手的优势也没用。
dir为2时,A就要去抢占后手的优势,B填完一个1将dir削减为1时,A填好剩下的1使整个串变为回文串,那么A又拥有了一个后手的优势。
dir为1时。
当0的个数为奇数时,当cnt为1时,A先采取翻转操作,B填剩下的1.cnt>1时,A先采取翻转操作,B填1使数组变为回文串,此时A填中间的那个1,数组仍为回文串,且A又抢占到了后手的优势。
0的个数为偶数时,cnt为2时,平局。cnt > 2时,A直接填1使数组变为回文串并且抢到后手的优势。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 1005;
const int MOD = 1e9 + 7;
typedef long long ll;
typedef pair<int, int> PII;
int t, n;
char s[N];
int main()
{
IOS
cin >> t;
while (t -- )
{
cin >> n;
cin >> s + 1;
int cnt = 0, dir = 0;
for (int i = 1; i <= n / 2; i ++ )
if (s[i] != s[n - i + 1]) dir ++;
for (int i = 1; i <= n; i ++ )
if (s[i] == '0') cnt ++;
if (dir == 0)
{
if (cnt == 1) cout << "BOB" << endl;
else if (cnt % 2 == 1) cout << "ALICE" << endl;
else cout << "BOB" << endl;
}
else if (dir >= 2) cout << "ALICE" << endl;
else
{
if (cnt & 1) cout << "ALICE" << endl;
else if (cnt == 2) cout << "DRAW" << endl;
else cout << "ALICE" << endl;
}
}
return 0;
}
C. Sequence Pair Weight
大佬说不难,可惜我是蒟蒻。
如果有个区间[i, j]i和j能构成一个逆序对,那么想数组的头尾扩展,一共能产生j*(n - i + 1)的贡献。如果又找到一个j1与j相等,那么又会产生j1*(n - i + 1)的贡献,因此能看出要统计下一个类似前缀和的东西。从前往后枚举区间终点。
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 100010;
const int MOD = 1e9 + 7;
typedef long long ll;
typedef pair<int, int> PII;
int t;
int n;
int a[N];
map<int, int> cnt;
map<int, ll> pre;
int main()
{
IOS
cin >> t;
while (t -- )
{
cnt.clear();
pre.clear();
cin >> n;
for (int i = 1; i <= n; i ++ )
cin >> a[i];
ll ans = 0;
for (int i = 1; i <= n; i ++ )
{
if (cnt[a[i]])
ans += (n - i + 1) * pre[a[i]];
cnt[a[i]] ++;
pre[a[i]] += i;
}
cout << ans << endl;
}
return 0;
}