zoukankan      html  css  js  c++  java
  • 题解 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;
    }
    
  • 相关阅读:
    两个数据库比较 对比视图存储过程及表结构差异
    导入/导出Excel
    比较两个数据库的表结构差异
    根据当月数据库自动生成下个月数据库--3
    根据当月数据库自动生成下个月数据库--2
    根据当月数据库自动生成下个月数据库--1
    ubuntu 分屏工具
    ubuntu 分屏工具
    中英文对照 —— 色彩的描述
    中英文对照 —— 色彩的描述
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12667357.html
Copyright © 2011-2022 走看看