题解 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
首先,\(n\leq 2\)的时候一定无解。因为格子之间总是可达的,无论怎么构造,后(Queen)和车(Rook)都不需要付钱。
考虑\(n=3\)的情况。我们可以把\(9\)放在\((3,2)\)的位置,然后想办法把后引到\((1,1)\)去。这样,在最后一步中,后就必须要付钱,完成跳跃了。
通过手玩,或者爆搜,可以构造出如下的\(3\times3\)棋盘:
8 7 6
5 1 2
4 9 3
在这个棋盘中,车不需要付钱,后需要付一次钱。
在\(n>3\)时,我们以上述的\(3\times3\)棋盘作为整个棋盘的左上角。通过构造,让车和后一起走完其他部分后,一起进入左上角。
例如:\(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\times3\)棋盘,但每个位置上的数要加上\(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\)。
也就是说,对于所有\(y\geq x\),选出的数中最多只有\(1\)个数是\(y\)的倍数。
从大到小枚举\(x\)。对于当前的\(x\),我们把它保留下来。则对于所有\(2x,3x,4x\dots\)就要被全部删去。我们给已经删去的数打上标记,就能知道哪些数是新被删掉的。也就是\(x\)能选出的数,相比于\(x+1\),减少了多少。
为什么对于所有\(x\)的倍数,只能留一个,我们选择留\(x\),而不留\(2x,3x,4x,\dots\)呢?因为\(2x,3x,4x,\dots\),可能同时也会作为其他\(y\geq x\)的\(y\)的倍数被留下来。\(x,y\)两个数留了同一个倍数,显然是亏了。
时间复杂度是调和级数,即\(O(n\log 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;
}