CodeForces 1300~1600补题

1476B 贪心 暴力

当前膨胀率不够加钱即可

1486D 二分 长度至少为k的子序列最大和

首先注意到以下性质:
设数组中的中位数至少为k。那么数组中为k的元素个数是要大于数组长度的一半的。证明显然。
同时,当数组的中位数至少为k时,那么它一定满足至少为k-1,k-2,...,可以使用二分的策略。
我们选择二分中位数的大小。
check函数采用以下策略:
当枚举的中位数为k时,将数组中大于等于k的元素化作+1,小于k的元素化为-1。这样区间和大于零即代表区间中大于k的元素个数多于k。
问题转化为,求一个长度至少为k的子序列的最大和。
这个问题可以采用前缀和的方法线性时间解决。
从k开始枚举区间右端点,并且对于每个下标i去查询i-k的前缀和是否更小,这个更小的前缀和可以用来更新后续的值,并且能够保证区间长度大于k。

题外话:长度至少为k的子序列的最小和可以用单调队列维护。

同样使用前缀和,但是区间左端点落在了一个固定长度的区间中。见滑动窗口问题。
本问题的左端点长短不固定,可以用线性算法解决。

#include <bits/stdc++.h>
// 
#define ios ios::sync_with_stdio(false), cin.tie(0);
#define et cout<<endl;
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
typedef unsigned long long ull;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int N = 2e5+5;
const int M = 2e5+5;
const int mod = 1e9+7;
//
int t, n, k;
int g[N];
int tmp[N];
bool check(int mid)
{
	for(int i=1; i<=n; i++)
	{
		if(g[i] >= mid) tmp[i] = 1;
		else tmp[i] = -1;
	}
	for(int i=1; i<=n; i++) tmp[i] += tmp[i-1];
	int lm = INT_MAX;
	int mx = INT_MIN;
	for(int i=k; i<=n; i++)
	{
		lm = min(lm, tmp[i-k]);
		mx = max(mx, tmp[i] - lm);
	}
	if(mx > 0) return true;
	else return false;
}
void solve()
{
	cin >> n >> k;
	for(int i=1; i<=n; i++) cin >> g[i];

	int l = 1, r = n;

	while(l<r)
	{
		int mid = l + r + 1>> 1;
		if(check(mid)) l=mid;
		else r=mid-1;
		// cout << l << endl;
	}

	cout << l << endl;
}
int main()
{
	ios
	// cin>>t;
	t = 1;
	while(t--)
	{
		solve();
	}
}

1476C dp

三种情况
都写在代码里了,主要是学习dp分析问题的方法

#include <bits/stdc++.h>
// 
#define ios ios::sync_with_stdio(false), cin.tie(0);
#define et cout<<endl;
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
typedef unsigned long long ull;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int N = 1e5+5;
const int M = 2e5+5;
const int mod = 1e9+7;
//
int t, n, k;
int g[N], a[N], b[N];
ll f[N];
void solve()
{
	cin >> n;
	for(int i=1; i<=n; i++) cin>>g[i];
	for(int i=1; i<=n; i++) cin>>a[i];
	for(int i=1; i<=n; i++) cin>>b[i];
	f[0] = 0;
	ll ans = -1;
	for(int i=2; i<=n; i++)
	{
		f[i] = g[i] + 1 + abs(a[i] - b[i]);
		if(a[i] != b[i]) f[i] = max(f[i], f[i] + f[i-1] - 2 * abs(a[i] - b[i]));
		ans = max(ans, f[i]);
	}
	cout << ans << endl;
}
int main()
{
	ios
	cin >> t;
	// t = 1;
	while(t--)
	{
		solve();
	}
}
/*状态表示
  f[i]: 以i为右端的一个环的长度
  属性: Max
  状态转移
  f[i]:
  1. 直接和左边形成一个环,接到左边直接接回来
  f[i] = ci + 1;
  2. 在左边从ai到bi走一段,显然比1要优
  f[i] = ci + 1 + |ai-1 - bi-1|
  3. 拆开左边的那个环, 当且仅当ai-1 != bi-1
  f[i] = ci + 1 + f[i-1]  - |ai-1 - bi-1|
*/

1475E 排序 组合数

水题

//1352G
#include <bits/stdc++.h>
// 
#define ios ios::sync_with_stdio(false), cin.tie(0);
#define et cout<<endl;
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
typedef unsigned long long ull;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int N = 1e3+5;
const int M = 2e5+5;
const int mod = 1e9+7;
//
int t, n, k;
int g[N], a[N], b[N];
int c[N][N];
void solve()
{
	cin >> n >> k;
	int x;
	map<int, int, greater<int>> mp;
	for(int i=1; i<=n; i++) cin >> x, mp[x] ++;

	ll res = 1;

	for(auto i: mp)
	{
		// cout << k << ' ' << i.second << endl;
		if(k - i.second < 0) res *= c[i.second][k];
		k -= i.second;
		if(k <= 0) break;
	}

	cout << res % mod << endl;
}
void init()
{
	for(int i=0; i<N; i++) c[0][i] = 0, c[i][0] = 1;

	for(int i=1; i<N; i++)
	{
		for(int j=1; j<N; j++)
			c[i][j] = (c[i-1][j] + c[i-1][j-1]) % mod;
	}
}
int main()
{
	ios
	cin >> t;
	init();
	// t = 1;
	while(t--)
	{
		solve();
	}
}

1463B 贪心

由观察,发现\([a_{1}, 1, a_{3}, 1 ...]\)\([1, a_{2}, 1, a_{4} ...]\)这样的数组是符合条件的。
这样只需找奇数和与偶数和谁小于S/2即可。

1466C 贪心

由观察,一个串是回文串当且仅当它中间的2或3长度的串是回文串,于是我们可以贪心地消去所有长度为2或3的回文串。
可以用垃圾字符标记已经替换掉的位置。

for(int i = 2; i <= len; i++)
{
	bool used_flag = false;
	if(s[i] == s[i-1] && !used[i-1]) used_flag = true;
	if(i > 2 && s[i] == s[i-2] && !used[i-2]) used_flag = true;
	used[i] = used_flag;
	ans += used[i];
}

1466D 思维 树

题意要求我们求每个颜色的连通分量点和的最大值的和。注意读题!
将树分割成多个连通分量是不值当的,于是我们考虑每次选一个权值最大的点给它的连通分量全部染色。
注意每个点只能被染色deg-1次,如果染色了deg次,那么最后一次染色是无意义的。

posted @ 2021-02-24 02:40  2wx  阅读(24)  评论(1)    收藏  举报