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,令 \(dp_{i,j}\) 表示从位置 \(i\) 到位置 \(j\) 一共的合法序列总情况数量。

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

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

  • \(dp_{i,j,0}\): 形态如 ***...* 的括号序列(即全部是*)。

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

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

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

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

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

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

  • \(dp_{l,r,0}\)(直接特判)
    没什么好解释的

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

  • \(dp_{l,r,2}=\sum\limits_{i=l}^{r-1} dp_{l,i,3}\times dp_{i+1,r,0}\)
    左边以括号序列开头且以括号序列结尾的是第3种,右边接一串*,是第0种。

  • \(dp_{l,r,3}=\sum\limits_{i=l}^{r-1} (dp_{l,i,2}+dp_{l,i,3})\times dp_{i+1,r,1}+dp_{l,r,1}\)
    左边以括号序列开头,结尾随便,符合的有第2和第3种,右边接一个括号序列,是第1种。
    记得加上直接一个括号序列的。

  • \(dp_{l,r,4}=\sum\limits_{i=l}^{r-1} (dp_{l,i,4}+dp_{l,i,5})\times dp_{i+1,r,1}\)
    左边以 * 开头,结尾随便,符合的有第4和第5种,右边接一个括号序列,是第1种。

  • \(dp_{l,r,4}\) 还有一种递推式也是可以的 , \(dp_{l,r,4}=\sum\limits_{i=l}^{r-1} (dp_{l,i,0}\times dp_{i+1,r,3})\)(亲测有效,可见两次提交记录 link1link2)

  • \(dp_{l,r,5}=\sum\limits_{i=l}^{r-1} dp_{l,i,4}\times dp_{i+1,r,0}+dp_{l,r,0}\)
    左边以* 开头,以括号序列结尾,符合的是第4种,右边接一串*,是第0种。
    记得加上全是 * 的。

最后,答案必须以括号序列开头,以括号序列结尾,所以直接是 \(dp_{1,n,3}\)

这样,初始状态也就没什么问题了,对于所有的 \(i\) 满足 \(1\le i \le n\),有 \(dp_{i,i-1,0}=1\)

最终时间复杂度 \(O(6\times n^3)\) 不到,(后半部分填不满 \(n^3\) )。

记得开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(2^{n/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] 交通规划

posted @ 2023-06-07 21:08  Aurora-JC  阅读(53)  评论(0编辑  收藏  举报