「Edu Codeforces Round」#131 (Div.2)

CF1701.

A. Grass Field

分析

\(0\) 个草地,答案为 \(0\)

\(1 \sim 3\) 个草地,答案为 \(1\)

\(4\) 个草地,答案为 \(2\)

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t, a[5][5];
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
void solve() {
	for(int i=1;i<=2;++i) for(int j=1;j<=2;++j) a[i][j]=read();
	int cnt0=0, cnt1=0;
	for(int i=1;i<=2;++i) for(int j=1;j<=2;++j) {
		if(a[i][j]) ++cnt1; else ++cnt0;
	}
	if(!cnt1) puts("0");
	else if(cnt1<4) puts("1");
	else puts("2");
}
int main() {
	t=read();
	while(t--) solve();
}

B. Permutation

分析

对于区间 \([1,n]\),其中 \(n \neq 1\),那么当 \(d = 2\) 的时候,成倍数关系的数对是最多的。直接输出 \(2\) 即可。

然后先把所有 \(1\)\([2,n]\) 之间的所有偶数加入排列,并把每个加入排列的数打标记。之后对于每个没有被打标记的数,用类似埃氏筛的方式筛去它和它的 \(2\) 次幂倍并加入排列。

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
int t, cnt, n, p[N];
bool v[N];
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
void solve() {
	cnt=0;
	memset(v,0,sizeof(v));
	n=read();
	printf("2\n");
	int cnt=0;
	for(int i=1;i<=n;i<<=1) v[i]=1, p[++cnt]=i;
	for(int i=3;i<=n;i+=2) if(!v[i]) {
		for(int j=i;j<=n;j<<=1) v[j]=1, p[++cnt]=j;
	}
	for(int i=1;i<=n;++i) printf("%d%c",p[i]," \n"[i==n]);
}
int main() {
	t=read();
	while(t--) solve();
}

C. Schedule Management

分析

工人工作是同时进行的,不论是贪心还是 DP 都做不了,我还是太弱了……

二分答案转判定,明明想到了的,却没写出来……

二分一个 \(mid\),表示用 \(mid\) 小时能否完成 \(m\) 个任务。对于每个工人,所有任务只有两种类型:精通的和不精通的。所以一个很显然的贪心策略就是,每一个任务都优先让精通它的工人去完成。记录 \(cnt_i\) 表示第 \(i\) 个工人精通的任务数量。

\(cnt_i < mid\) 时,\(i\) 不仅能够用 \(cnt_i\) 小时完成所有精通的任务,还有时间完成其他的任务,可是其他的任务只能用 \(\frac{1}{2}\) 的效率来完成,所以这种情况下一共能完成的任务数量为 \(cnt_i + \frac{mid - cnt_i}{2}\)

否则只能够完成 \(cnt_i\) 个任务。

是不是一想就明白了呢?我还是趁早退役咯~

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
int t, n, m, a[N], cnt[N];
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
bool check(int x) {
	ll tot=0;
	for(int i=1;i<=n;++i) {
		if(cnt[i]<x) tot+=cnt[i]+(x-cnt[i])/2;
		else tot+=x;
	}
	return tot>=m;
}
void solve() {
	memset(cnt,0,sizeof(cnt));
	n=read(), m=read();
	for(int i=1;i<=m;++i) ++cnt[a[i]=read()];
	int l=0, r=0;
	for(int i=1;i<=n;++i) r=max(r,cnt[i]*2);
	while(l<r) {
		int mid=(l+r)/2;
		if(check(mid)) r=mid; else l=mid+1;
	}
	
	printf("%d\n",l);
}
int main() {
	t=read();
	while(t--) solve();
}

D. Permutation Restoration

分析

下文除法均向下取整。

对于每个 \(b_i\),都有一个「决策区间」。意思是,只要 \(a_i\) 属于这个区间,那么就一定满足条件。

考虑求出这个区间,当 \(b_i\)\(0\) 的时候,只需要保证 \(i < a_i\) 就好了,所以决策区间的左端点是 \(i+1\),右端点一定是 \(n\)

\(b_i > 0\) 时,其左右端点一定在 \([1,i]\) 里面,那就查找最小的使得 \(\frac{i}{x} \le b_i\)\(x\) 作为做端点(因为 \(x\) 越小左式越大),查找最大的使得 \(\frac{i}{x} \ge b_i\)\(x\) 作为右端点(因为 \(x\) 越大左式越小)。可以使用二分查找。

打表发现,如果 \(b_i > 0\),那么左端点为 \(\frac{i}{b_i +1} +1\),右端点为 \(\frac{i}{b_i}\)。证明?不会。

然后贪心,对于一个 \(i\),将所有以它为左端点的元素加入决策集合,排除所有右端点小于 \(i\) 的元素,找到右端点最小的 \(x\) 并令 \(a_x = i\)

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define PII pair<int,int>
#define fst first
#define sec second
#define pb push_back
const int N=5e5+5;
int t, n, m, b[N], d[N], ans[N];
priority_queue<PII > q;
vector<int> v[N];
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
void bsearch(int a,int b,int x,int& l,int& r) {
	int L=a, R=b;
	while(L<R) {
		int mid=(L+R)/2;
		if(b/mid<=x) R=mid; else L=mid+1; 
	}
	l=L;
	L=a, R=b;
	while(L<R) {
		int mid=(L+R+1)/2;
		if(b/mid>=x) L=mid; else R=mid-1;
	}
	r=L;
}
void solve() {
	n=read();
	for(int i=1;i<=n;++i) {
		b[i]=read();
		int l, r;
		if(!b[i]) { l=i+1, r=n; goto record; }
		// bsearch(1,i,b[i],l,r);
		l=i/(b[i]+1)+1, r=i/b[i];
		record: v[l].pb(i), d[i]=r;
		// printf("b[%d]=%d l: %d r: %d\n",i,b[i],l,r);
	}
	for(int i=1;i<=n;++i) {
		for(int j=0;j<v[i].size();++j)
			q.push(make_pair(-d[v[i][j]],v[i][j]));
		if(-q.top().fst<i) q.pop();
		ans[q.top().sec]=i, q.pop();
	}
	for(int i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]), v[i].clear();
}
int main() {
	t=read();
	while(t--) solve();
}

E. Text Editor

没看,不会。

F. Points

没看,不会。


「Edu Codeforces Round」#131 (Div.2)
https://yozora0908.github.io/2022/cf1701-solution/
作者
yozora0908
发布于
2022年7月9日
许可协议