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;
}