题解 CF1333 D,E,F Codeforces Round #632 (Div. 2)

比赛链接

CF1333D Challenges in school №41

把向左的人看做是斜向上走一个单位,向右的人看做是斜向下走一个单位,则原序列可以转化为一个折线图。如图,是序列RLRL的折线图:

一次操作,相当于是把一段形如/的折线翻折成/

我们的目标是要让最终的图形成为一个单峰的形状。

手玩一下,显然,我们应该先从最下面的一层翻起。每次消除掉深度最低的一整层/。暴力模拟这个过程,每次找到最底下的一层/并翻上去。时间复杂度(O(n^2))。这样我们就求出了操作次数最少的翻折方法。

考虑如何把这个翻折方法的操作数增加到(k)。只要有一层的操作数量大于(1),我们就把这一层的第一个操作独立出来。直到总操作数量达到(k)

时间复杂度(O(n^2))

参考代码:

//problem:CF1333D
#include <bits/stdc++.h>
using namespace std;
 
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
 
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
 
/*  ------  by:duyi  ------  */ // myt天下第一
const int MAXN=3000;
int n,K,dep[MAXN+5];
char s[MAXN+5];
int main() {
	scanf("%d%d%s",&n,&K,s+1);
	for(int i=1;i<=n;++i){
		if(s[i]=='L')dep[i]=dep[i-1]+1;
		else dep[i]=dep[i-1]-1;
	}
	vector<vector<int> >vv;
	while(1){
		vector<int>vec;
		int mindep=n;
		for(int i=1;i<n;++i){
			if(!(s[i]=='R'&&s[i+1]=='L'))continue;
			if(dep[i]<mindep){
				vector<int>().swap(vec);
				mindep=dep[i];
			}
			if(dep[i]==mindep){
				vec.pb(i);
			}
		}
		if(!SZ(vec))break;
		for(int i=0;i<SZ(vec);++i){
			s[vec[i]]='L';
			s[vec[i]+1]='R';
		}
		for(int i=1;i<=n;++i){
			if(s[i]=='L')dep[i]=dep[i-1]+1;
			else dep[i]=dep[i-1]-1;
		}
		vv.pb(vec);
	}
	//for(int i=1;i<=n;++i)cout<<s[i];cout<<endl;
	if(!SZ(vv)||SZ(vv)>K){puts("-1");return 0;}
	K-=SZ(vv);
	vector<vector<int> >ans;
	for(int i=0;i<SZ(vv);++i){
		int p=0;
		for(int j=0;j<SZ(vv[i])-1;++j){
			if(K){
				vector<int>tmp;
				tmp.pb(vv[i][j]);
				ans.pb(tmp);
				p=j+1;
				--K;
			}
			else break;
		}
		ans.pb(vector<int>(vv[i].begin()+p,vv[i].end()));
	}
	if(K){puts("-1");return 0;}
	for(int i=0;i<SZ(ans);++i){
		printf("%d ",SZ(ans[i]));
		for(int j=0;j<SZ(ans[i]);++j){
			printf("%d ",ans[i][j]);
		}
		puts("");
	}
	return 0;
}

CF1333E Road to 1600

首先,(nleq 2)的时候一定无解。因为格子之间总是可达的,无论怎么构造,后(Queen)和车(Rook)都不需要付钱。

考虑(n=3)的情况。我们可以把(9)放在((3,2))的位置,然后想办法把后引到((1,1))去。这样,在最后一步中,后就必须要付钱,完成跳跃了。

通过手玩,或者爆搜,可以构造出如下的(3 imes3)棋盘:

8 7 6
5 1 2
4 9 3

在这个棋盘中,车不需要付钱,后需要付一次钱。

(n>3)时,我们以上述的(3 imes3)棋盘作为整个棋盘的左上角。通过构造,让车和后一起走完其他部分后,一起进入左上角。

例如:(n=4,n=5)时,可以分别如下构造:

// n=4
0 0 0 6
0 0 0 7
0 0 0 5
1 2 3 4

// n=5
0  0  0  15 1
0  0  0  16 2
0  0  0  14 3
10 11 12 13 4
9  8  7  6  5

其中(0)的部分,用于填入之前构造好的(3 imes3)棋盘,但每个位置上的数要加上(n^2-9)

(n)(5)更大的情况是类似的,每次在外围加一圈即可。

时间复杂度(O(n^2))

参考代码:

//CF1333E
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

/*  ------  by:duyi  ------  */ // myt天下第一
const int b[4][4]={{0,0,0,0},{0,8,7,6},{0,5,1,2},{0,4,9,3}};
int n,ans[505][505];
int main() {
	cin>>n;
	if(n<=2){puts("-1");return 0;}
	if(n==3){
		for(int i=1;i<=3;++i){
			for(int j=1;j<=3;++j){
				cout<<b[i][j]<<" ";
			}
			cout<<endl;
		}
		return 0;
	}
	bool cur=((n-3)&1);
	int cnt=0;
	for(int i=n;i>3;--i){
		if(cur){
			//左下到右上
			for(int j=1;j<=i;++j)ans[i][j]=++cnt;
			for(int j=i-1;j>=1;--j)ans[j][i]=++cnt;
		}
		else{
			//右上到左下
			for(int j=1;j<=i-1;++j)ans[j][i]=++cnt;
			for(int j=i;j>=1;--j)ans[i][j]=++cnt;
		}
		cur^=1;
	}
	swap(ans[1][4],ans[2][4]);
	for(int i=1;i<=3;++i)for(int j=1;j<=3;++j)ans[i][j]=b[i][j]+cnt;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			cout<<ans[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

CF1333F Kate and imperfection

显然,答案是单调不降的。

我们从大到小枚举答案(x),考虑哪个位置之前的答案小于(x)。比如,如果我们知道了位置([1,p])的答案小于(x),我们就令(ans[p]=x-1)。初始时(ans)数组值全部为(n)。这样,我们最后对(ans)数组求后缀(min),就能得到答案了。

哪个位置之前的答案小于(x)?这个问题等价于,最多能从({1,dots,n})中选多少个数,使得两两的(gcd)都小于(x)

也就是说,对于所有(ygeq x),选出的数中最多只有(1)个数是(y)的倍数。

从大到小枚举(x)。对于当前的(x),我们把它保留下来。则对于所有(2x,3x,4xdots)就要被全部删去。我们给已经删去的数打上标记,就能知道哪些数是新被删掉的。也就是(x)能选出的数,相比于(x+1),减少了多少。

为什么对于所有(x)的倍数,只能留一个,我们选择留(x),而不留(2x,3x,4x,dots)呢?因为(2x,3x,4x,dots),可能同时也会作为其他(ygeq x)(y)的倍数被留下来。(x,y)两个数留了同一个倍数,显然是亏了。

时间复杂度是调和级数,即(O(nlog n))

参考代码:

//problem:CF1333F
#include <bits/stdc++.h>
using namespace std;
 
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
 
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
 
/*  ------  by:duyi  ------  */ // myt天下第一
const int MAXN=5e5;
int n,ans[MAXN+5];
bool vis[MAXN+5];
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;++i)ans[i]=n;
	int res=n;
	for(int i=n;i>=2;--i){
		int cnt=0;
		for(int j=2;j*i<=n;++j)if(!vis[i*j])cnt++,vis[i*j]=1;
		res-=cnt;
		ans[res]=i-1;
	}
	for(int i=n-1;i>=2;--i)ans[i]=min(ans[i],ans[i+1]);
	for(int i=2;i<=n;++i)printf("%d ",ans[i]);puts("");
	return 0;
}
原文地址:https://www.cnblogs.com/dysyn1314/p/12667357.html