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次,那么最后一次染色是无意义的。

浙公网安备 33010602011771号