Educational Codeforces Round 131 (Rated for Div. 2)
C
核心思路
这个题目乍一看是一个模拟题其实这是一个可以使用二分的题目,因为这其实只需要我们找到答案就好了。
那么二分怎么去check呢,我们可以计算出来每一位工人在x小时可以完成的任务量。
首先我们可以搞出来某一个工人擅长哪几种任务,对于这几种任务它可以一个小时就完成一个,而对于他不擅长的则需要两个小时才可以完成一个。
于是我们只需要看他们是否大于我们规定的任务量就好了。
// Problem: C. Schedule Management
// Contest: Codeforces - Educational Codeforces Round 131 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1701/problem/C
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define NO {puts("NO") ; return ;}
#define YES {puts("YES") ; return ;}
#define endl "\n"
#define int long long
const int INF = 0x3f3f3f3f;
const int N=2e5+10;
int cnt[N];
int n,m;
int check(int x)
{
int sum=0;
for(int i=1;i<=n;i++)
{
if(cnt[i]>=x)
sum+=x;
else
sum+=cnt[i]+(x-cnt[i])/2;
}
return sum>=m;
}
void solve()
{
cin>>n>>m;
memset(cnt,0,sizeof cnt);
for(int i=1;i<=m;i++)
{
int x;
cin>>x;
cnt[x]++;
}
int l=1,r=2*m;
while(l<r)
{
int mid=l+r>>1;
if(check(mid))
r=mid;
else
l=mid+1;
}
cout<<l<<endl;
}
signed main()
{
int t;
cin>>t;
while(t--)
{
solve();
}
}
D
核心思路
其实我们首先应该需要推导出来的是\(a_i\)的表达式,因为\(b_i=\lfloor{i/a_i}\rfloor\).所以\(a_i*b_i\leq i < a_i*(b_i+1)\).
所以我们可以得出来\(\lfloor{i/(b_i+1)}\rfloor+1 \leq a_i \leq \frac{i}{b_i}\).
所以我们可以得出来每一个位置的一个取值范围,然后我们把每一个数可以填入的左边界先预处理出来,比如可以填入1这个数组的位置一定是\([1,2],[1,3]...\).然后我们贪心这个位置的右边界,这个是什么意思呢,也就是这个位置所对应的最小有边界,也就是在上面那几个区间里面就选择\([1,2]\)这个区间的位置来填入我们的数字1。
这样做是为了不影响后面的填数。
// Problem: D. Permutation Restoration
// Contest: Codeforces - Educational Codeforces Round 131 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1701/problem/D
// Memory Limit: 256 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define NO {puts("NO") ; return ;}
#define YES {puts("YES") ; return ;}
#define endl "\n"
#define int long long
const int INF = 0x3f3f3f3f;
const int N=5e5+10;
int l[N],r[N];
int a[N],b[N];
struct Node{
int x,id;
bool operator < (const Node& t) const {
return x>t.x;
}
};
priority_queue<Node> q;
int n;
vector<int> c[N];
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>b[i];
if(!b[i])
{
l[i]=i+1;
r[i]=n;
}
else
{
l[i]=((i/(b[i]+1))+1);
r[i]=i/b[i];
}
c[l[i]].push_back(i);
}
// for(int i=1;i<=n;i++)
// cout<<l[i]<<" "<<r[i]<<endl;
while(!q.empty())
q.pop();
for(int i=1;i<=n;i++)
{
for(int x:c[i])
q.push((Node){r[x],x});
Node temp=q.top();
// cout<<temp.x<<" "<<temp.id<<endl;
q.pop();
a[temp.id]=i;
}
for(int i=1;i<=n;i++)
{
cout<<a[i]<<" ";
c[i].clear();
}
cout<<endl;
}
signed main()
{
int t;
cin>>t;
while(t--)
{
solve();
}
}
E
核心思路
这个题目根据数据范围可以看出来这是一个dp的题目,那么怎么设计状态表示呢,我们可以通过模拟小规模样例。
首先我们知道肯定要有状态表示当前光标移动到了a串的什么位置和b串的什么位置。还有就是需要注意我们的光标是可以跳跃的,所以我们可以把我们的整个字符串分为前中后三个位置,方便我们的状态转移。
集合定义
\(f[i][j][0/1/2]表示的是当前处理到了a的第i个字符和b的第j个字符,当前光标在前中后部分的最少移动次数\)
集合划分
- 我们知道任意时刻我们都是可以删除a中的字符的,但是要注意如果我们在光标在前面的部分,我们需要花费代价2因为先要往右移动才可以删除字符。
- 如果\(a_i=b_j\).如果如果在前部分那么直接往右移动匹配长度加1,如果是后面就需要往左移动匹配长度加1.如果是在中间,那么就由前面的状态转移过来。
- 这里还有一个需要注意的是,任意时刻光标都可以从前半部分转入中/后部分,或者从中部分转入后部分。
最后还需要按下home操作归还原理啊。操作3我不是很理解,估计是模拟样例退出来的。
/*
p_b_p_b txdy
AThousandMoon txdy
AThousandSuns txdy
hxy txdy
*/
#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
const int maxn = 5050;
const int inf = 0x3f3f3f3f;
int n, m, f[2][maxn][3];
char s[maxn], t[maxn];
void solve() {
scanf("%d%d%s%s", &n, &m, s + 1, t + 1);
int j = 0;
for (int i = 1; i <= n; ++i) {
if (j < m && t[j + 1] == s[i]) {
++j;
}
}
if (j < m) {
puts("-1");
return;
}
int pl = m;
for (int i = 1; i <= m; ++i) {//这里是指直接删除后面的部分。
if (s[i] != t[i]) {
pl = i - 1;
break;
}
}
int cur = 0;
for (int i = 0; i <= m; ++i) {
f[0][i][0] = f[0][i][1] = f[0][i][2] = inf;
}
f[0][0][0] = f[0][0][1] = f[0][0][2] = 0;
for (int i = 1; i <= n; ++i) {
cur ^= 1;
for (int j = 0; j <= m; ++j) {
f[cur][j][0] = f[cur][j][1] = f[cur][j][2] = inf;
}
for (int j = 0; j <= m; ++j) {
f[cur][j][0] = min(f[cur][j][0], f[cur ^ 1][j][0] + 2);
f[cur][j][2] = min(f[cur][j][2], f[cur ^ 1][j][2] + 1);
if (j && s[i] == t[j]) {
f[cur][j][0] = min(f[cur][j][0], f[cur ^ 1][j - 1][0] + 1);
f[cur][j][1] = min(f[cur][j][1], f[cur ^ 1][j - 1][1]);
f[cur][j][2] = min(f[cur][j][2], f[cur ^ 1][j - 1][2] + 1);
}
}
for (int j = 0; j <= m; ++j) {
f[cur][j][1] = min(f[cur][j][1], f[cur][j][0]);
f[cur][j][2] = min(f[cur][j][2], f[cur][j][1]);
}
}
printf("%d\n", min(n - pl, f[cur][m][2] + 1));
}
int main() {
int T = 1;
scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号