我需要一个答案,也|

Aurora-JC

园龄:3年粉丝:3关注:4

CSP-S 2021

P7913 [CSP-S 2021] 廊桥分配

让我们先忽略廊桥数量的限制来安排航班。我们维护一个空闲的廊桥队列,每到达一架航班,就给它安排编号最小的廊桥供其使用。

现在加上廊桥数量的限制。容易发现刚才的廊桥分配方法直接就帮我们解决了廊桥限制的问题:如果当前有 n 个廊桥可供使用,则分配到 n+1 号及以后的廊桥实质上就是分配到远机位了,不需要再做任何额外的处理。

到这里做法就很清晰了:我们按照开头提到的分配方法来安排航班的停靠位置,记录各廊桥停靠的航班数,做一个前缀和,最后枚举分配给某个区的廊桥数,算出各情况下两区实际使用廊桥的航班数总和,即可解决本题。

第一篇题解写的挺好的,自己就不打了,嘿嘿。

code

#include<bits/stdc++.h>
#define N 100005
#define X first
#define Y second
#define pii pair<int,int>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
struct node{
int l,r;
bool operator < (const node& A) const{return l==A.l?r<A.r:l<A.l;}
}a[N],b[N];
int n,m1,m2,ans;
int res1[N],res2[N];
void solve(node* t,int m,int* res){
priority_queue<pii,vector<pii>,greater<pii> > lq;
priority_queue<int,vector<int>,greater<int> > wq;
for(int i=1;i<=n;++i) wq.push(i);
for(int i=1;i<=m;++i){
while(!lq.empty()&&t[i].l>=lq.top().X){
wq.push(lq.top().Y);lq.pop();
}
if(wq.empty()) continue;
int x=wq.top();
wq.pop(),res[x]++;
lq.push(make_pair(t[i].r,x));
}
for(int i=1;i<=n;++i) res[i]+=res[i-1];
}
int main(){
n=read(),m1=read(),m2=read();
for(int i=1;i<=m1;++i) a[i].l=read(),a[i].r=read();
for(int i=1;i<=m2;++i) b[i].l=read(),b[i].r=read();
sort(a+1,a+1+m1),sort(b+1,b+1+m2);
solve(a,m1,res1),solve(b,m2,res2);
for(int i=0;i<=n;++i) ans=max(res1[i]+res2[n-i],ans);
printf("%d\n",ans);
return 0;
}

P7914 [CSP-S 2021] 括号序列 :

首先肯定是区间dp,令 dpi,j 表示从位置 i 到位置 j 一共的合法序列总情况数量。

但是不同的形态可能会有不同的转移,如:(S)这种只能从S转移过来等等。所以只开两维的dp状态必然是不够的。

可以将两位的dp扩充为三维,第三位表示不同的形态种类,dp状态就变成了 dpi,j,[0,5]。没种状态表示:

  • dpi,j,0: 形态如 ***...* 的括号序列(即全部是*)。

  • dpi,j,1: 形态如 (...)的括号序列(即左右直接被括号包裹且最左边括号与最右边的括号相互匹配)。

  • dpi,j,2: 形态如 (...)**(...)*** 的括号序列(即左边以括号序列开头,右边以*结尾)。

  • dpi,j,3: 形态如 (...)***(...)*(...) 的括号序列(即左边以括号序列开头,右边以括号序列结尾,注意:第2种形态也属于这种形态)。

  • dpi,j,4: 形态如 ***(...)**(...) 的括号序列(即左边以*开头,右边以括号序列结尾)。

  • dpi,j,5: 形态如 ***(...)**(...)** 的括号序列(即左边以*开头,右边以*结尾,注意:第1种形态也属于这种形态)。

设定完状态以后,转移就直接出来了,注意:为了防止连续超过 k*一起出现,转移的时候不能把两段*拼接起来,在状态1的时候暴力判断一下两端的距离是否是 k 的,是的才能转移。

  • dpl,r,0(直接特判)
    没什么好解释的

  • dpl,r,1=(dpl+1,r1,0+dpl+1,r1,2+dpl+1,r1,3+dpl+1,r1,4)×compare(l,r),
    compare(i,j) 表示第 i 位与第 j 位能否配对成括号,能则为 1,否则为 0
    加括号时,里面可以是全*,可以是有一边是 * , 也可以是两边都不是 * ,唯独不能两边都是 * 且中间有括号序列。

  • dpl,r,2=i=lr1dpl,i,3×dpi+1,r,0
    左边以括号序列开头且以括号序列结尾的是第3种,右边接一串*,是第0种。

  • dpl,r,3=i=lr1(dpl,i,2+dpl,i,3)×dpi+1,r,1+dpl,r,1
    左边以括号序列开头,结尾随便,符合的有第2和第3种,右边接一个括号序列,是第1种。
    记得加上直接一个括号序列的。

  • dpl,r,4=i=lr1(dpl,i,4+dpl,i,5)×dpi+1,r,1
    左边以 * 开头,结尾随便,符合的有第4和第5种,右边接一个括号序列,是第1种。

  • dpl,r,4 还有一种递推式也是可以的 , dpl,r,4=i=lr1(dpl,i,0×dpi+1,r,3)(亲测有效,可见两次提交记录 link1link2)

  • dpl,r,5=i=lr1dpl,i,4×dpi+1,r,0+dpl,r,0
    左边以* 开头,以括号序列结尾,符合的是第4种,右边接一串*,是第0种。
    记得加上全是 * 的。

最后,答案必须以括号序列开头,以括号序列结尾,所以直接是 dp1,n,3

这样,初始状态也就没什么问题了,对于所有的 i 满足 1in,有 dpi,i1,0=1

最终时间复杂度 O(6×n3) 不到,(后半部分填不满 n3 )。

记得开long long,并且取模。

code

#include<bits/stdc++.h>
#define N 505
#define mod 1000000007
#define int long long
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,k;
char s[N];
int f[N][N][6];
// f 0 ***
// f 1 ()
// f 2 ()***
// f 3 ()**() 包含 f 1
// f 4 ***()**()
// f 5 ***()**()*** 包含 f 0
bool check(int a,int b){return (s[a]=='('||s[a]=='?')&&(s[b]==')'||s[b]=='?');}
signed main(){
n=read(),k=read();
scanf("%s",s+1);
for(int i=1;i<=n;++i) f[i][i-1][0]=1;
for(int l=1;l<=n;++l){
for(int i=1;i+l-1<=n;++i){
int j=i+l-1;
if(l<=k) f[i][j][0]=f[i][j-1][0]&&(s[j]=='?'||s[j]=='*');
if(l>=2){
if(check(i,j)) f[i][j][1]=(f[i+1][j-1][0]+f[i+1][j-1][2]+f[i+1][j-1][3]+f[i+1][j-1][4])%mod;
for(int k=i;k<j;++k){
f[i][j][2]=(f[i][j][2]+f[i][k][3]*f[k+1][j][0])%mod;
f[i][j][3]=(f[i][j][3]+(f[i][k][2]+f[i][k][3])*f[k+1][j][1])%mod;
// f[i][j][4]=(f[i][j][4]+(f[i][k][4]+f[i][k][5])*f[k+1][j][1])%mod; //两种均可
f[i][j][4]=(f[i][j][4]+f[i][k][0]*f[k+1][j][3])%mod;
f[i][j][5]=(f[i][j][5]+f[i][k][4]*f[k+1][j][0])%mod;
}
}
f[i][j][5]=(f[i][j][5]+f[i][j][0])%mod;
f[i][j][3]=(f[i][j][3]+f[i][j][1])%mod;
}
}
printf("%lld\n",f[1][n][3]);
return 0;
}

P7915 [CSP-S 2021] 回文

枚举 b 中开头的值(显然它只能是 a 的开头和末位),然后在 a 中找到与之对应的值 (即 b 的末位),这就相当于一个区间往里面缩,一个区间往外拓展,这样搜索的复杂度是 O(2n/2)

考虑对其再来一个贪心,显然优先级为 LL>LR>RL>RR,只要有一个可行我们就只搜这一条路,那么复杂度为 O(n) (每次只移动一次,每移动一次数组长度 2 )。

code

#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int T,n;
char ans[N];
int a[N],b[N],L[N],R[N];
void init(){
memset(b,0,sizeof(b));
memset(ans,0,sizeof(ans));
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
}
bool solve(int lst,int Li,int Ri){
if(Li==2) ans[1]='L';
else ans[1]='R';
int l=lst,r=lst,bg=2,ed=n-1;
for(int i=1;i<n/2;++i){
if(R[a[Li]]==l-1) l--,Li++,ans[bg++]='L',ans[ed--]='L';
else if(R[a[Li]]==r+1) r++,Li++,ans[bg++]='L',ans[ed--]='R';
else if(L[a[Ri]]==l-1) l--,Ri--,ans[bg++]='R',ans[ed--]='L';
else if(L[a[Ri]]==r+1) r++,Ri--,ans[bg++]='R',ans[ed--]='R';
else return 0;
}
ans[n]='L';
for(int i=1;i<=n;++i) printf("%c",ans[i]);printf("\n");
return 1;
}
signed main(){
T=read();
while(T--){
init();
n=read()*2;
for(int i=1;i<=n;++i) a[i]=read(),L[a[i]]?R[a[i]]=i:L[a[i]]=i;
if(solve(R[a[1]],2,n)) continue;
if(solve(L[a[n]],1,n-1)) continue;
printf("-1\n");
}
return 0;
}

P7916 [CSP-S 2021] 交通规划

本文作者:南风未起

本文链接:https://www.cnblogs.com/jiangchen4122/p/17464587.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Aurora-JC  阅读(57)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起