20221025
20221024(csp-s 2021)
廊桥分配
考这道题时候想到了一个运用二分的O(nlogn)的算法,但是看漏了先到先得的条件,最后打了个暴力,喜提20pts
思路
很容易发现对于每个已经在廊桥中的飞机,下一个也能进入这个廊桥的飞机一定是到达时间大于等于已经在廊桥中的飞机的离开时间的飞机中,到达时间最早的。那我们就可以根据这个条件处理出第\(i\)个廊桥能停靠的最大飞机数。这个东西直接求的时间复杂度大概是\(O(n^{2})\)的,那么我们来思考怎样快速求出它。可以发现,按左端点排序后就可以运用二分快速求出下一个也能进入同样廊桥的飞机了,这样处理后时间复杂度就优化到了\(O(nlogn)\)的了,这个过程用\(set\)可以很方便地完成。这样我们就成功与处理出了国际区于国内区第\(i\)个廊桥能停靠的最多飞机数了。最后再通过后缀和求一遍答案即可。
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
x=0;int f=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
x=f?~x+1:x;
}
template<typename T>inline void out(T x){
if(x<0)x=~x+1,putchar('-');
if(x>9)out(x/10);
putchar(x%10^48);
}
const int M=1e5+5;
struct plane{
int a,b;
bool operator <(const plane &x)const{return a<x.a;}
}p1[M],p2[M];
int n,m1,m2;
int ans1[M],ans2[M];
set <plane> s;
int main(){
in(n),in(m1),in(m2);
fo(i,1,m1)in(p1[i].a),in(p1[i].b);
fo(i,1,m2)in(p2[i].a),in(p2[i].b);
fo(i,1,m1)s.insert(p1[i]);
fo(i,1,n){
int pos=0,ans=0;
while(1){
auto it=s.lower_bound({pos,0});
if(it==s.end())break;
pos=it->b;
s.erase(it);
++ans;
}
ans1[i]=ans1[i-1]+ans;
}
s.clear();
fo(i,1,m2)s.insert(p2[i]);
fo(i,1,n){
int pos=0,ans=0;
while(1){
auto it=s.lower_bound({pos,0});
if(it==s.end())break;
pos=it->b;
s.erase(it);
++ans;
}
ans2[i]=ans2[i-1]+ans;
}
int G=0;
fo(i,0,n)G=max(G,ans1[i]+ans2[n-i]);
out(G);
return 0;
}
回文
不太聪明の做法
因为要操作序列的字典序最小,而L
<R
,所以我们只需要每次尽量选择操作\(1\),并且在排除所有非法方案后,得到的第一个答案就是我们的最终答案。
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
x=0;int f=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
x=f?~x+1:x;
}
template<typename T>inline void out(T x){
if(x<0)x=~x+1,putchar('-');
if(x>9)out(x/10);
putchar(x%10^48);
}
const int N=500005;
int n;
int a[N<<1],b[N<<1],act[N<<1],tmp[N<<1];
bool f;
inline void dfs(int l,int r,int now){
if(f)return;
if(l>r){
if(b[1]!=b[n])return;
fo(i,1,n)tmp[i]=act[i];
f=1;
return;
}
if(now>(n>>1)&&b[now]!=b[n-now+1])return;
b[now+1]=a[l];
act[now+1]=1;//L
dfs(l+1,r,now+1);
b[now+1]=a[r];
act[now+1]=2;//R
dfs(l,r-1,now+1);
}
int main(){
int t;
in(t);
while(t--){
f=0;
in(n);
if(n>20){
puts("-1");
continue;
}
n<<=1;
fo(i,1,n)in(a[i]);
dfs(1,n,0);
if(!f){
puts("-1");
continue;
}
fo(i,1,n)printf("%c",tmp[i]==1?'L':'R');
puts("");
}
return 0;
}
聪明の做法
先来深入地理解一下每次的操作。先只考虑操作\(1\),当我们第一次操作进行操作\(1\)之后,为了保证构成的数列是回文数列,我们就需要保证,第一次操作取出来的数\(x_{1}\)在还未操作的数中\(x_{2}\)最后一个被取出。而对于除了\(x_{2}和x_{1}\)之外的数就相当于构成了两个栈,因为每次操作只能删去最右或最左的数,如图:
我们只需要让这两个栈中的数在保证回文性质的同时在第二个\(4\)之前全部出栈即可。那么我们继续来考虑怎么做到这个操作。继续思考怎么在出栈一个数的同时维护回文性质,比如,我们让左栈的\(1\)出栈,为了维护回文性质就需要让另外一个\(1\)在最后出栈,也就是在这两个栈的栈底出栈,说是栈,其实更像是双端队列。这样我们的大致思路便出来了。在满足尽量让左边先出的原则的基础上,按这种方式操作,如果怎么操作都不行,则为无解。
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
x=0;int f=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
x=f?~x+1:x;
}
template<typename T>inline void out(T x){
if(x<0)x=~x+1,putchar('-');
if(x>9)out(x/10);
putchar(x%10^48);
}
const int N=5e5+5;
char ans[N<<1];
int n;
int a[N<<1],l[N<<1],r[N<<1],cntl,headl,cntr,headr,cnt;
inline void init(){
in(n);
n<<=1;
fo(i,1,n)in(a[i]);
}
int t;
inline bool L(){
cntl=cntr=cnt=0;
headl=headr=1;
fo(i,1,n)ans[i]=l[i]=r[i]=0;
int pos;
ans[++cnt]='L';
ans[n]='L';
fo(i,2,n)if(a[i]==a[1]){pos=i;break;}
for(int i=pos-1;i>=2;--i)l[++cntl]=a[i];
for(int i=pos+1;i<=n;++i)r[++cntr]=a[i];
fo(i,1,(n-2)>>1){
if(cntl-headl>=1&&l[cntl]==l[headl])++headl,--cntl,ans[++cnt]='L',ans[n-cnt+1]='L';
else if(cntr-headr>=1&&r[cntr]==r[headr])++headr,--cntr,ans[++cnt]='R',ans[n-cnt+1]='R';
else if(l[cntl]==r[headr])--cntl,++headr,ans[++cnt]='L',ans[n-cnt+1]='R';
else if(r[cntr]==l[headl])--cntr,++headl,ans[++cnt]='R',ans[n-cnt+1]='L';
else return 0;
}
return 1;
}
inline bool R(){
cntl=cntr=cnt=0;
headl=headr=1;
fo(i,1,n)ans[i]=l[i]=r[i]=0;
int pos;
ans[++cnt]='R';
ans[n]='L';
fo(i,1,n-1)if(a[i]==a[n]){pos=i;break;}
for(int i=pos-1;i>=1;--i)l[++cntl]=a[i];
for(int i=pos+1;i<n;++i)r[++cntr]=a[i];
fo(i,1,(n-2)>>1){
if(cntl-headl>=1&&l[cntl]==l[headl])++headl,--cntl,ans[++cnt]='L',ans[n-cnt+1]='L';
else if(cntr-headr>=1&&r[cntr]==r[headr])++headr,--cntr,ans[++cnt]='R',ans[n-cnt+1]='R';
else if(l[cntl]==r[headr])--cntl,++headr,ans[++cnt]='L',ans[n-cnt+1]='R';
else if(r[cntr]==l[headl])--cntr,++headl,ans[++cnt]='R',ans[n-cnt+1]='L';
else return 0;
}
return 1;
}
inline bool work(){
init();
if(L())return 1;
if(R())return 1;
return 0;
}
int main(){
in(t);
while(t--){
if(work()){
fo(i,1,n)printf("%c",ans[i]);
putchar('\n');
}
else puts("-1");
}
return 0;
}