CF 刷题

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值