【补题计划】CSP-S 2021
【CSP-S 2021】补题记录
T1 【CSP-S 2021】廊桥分配
这明显就不是普通的签到题啊(记得当时我刚学OI 只会数组和for循环搞出来15pts)
现在看题解发现还是很简单滴,就是用PQ模拟一下再维护一下前缀和然后枚举一下
但是当年在考场上好像因为长得就像一个贪心模板然后坑了不少人
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<algorithm>
#define pii pair<int,int>
using namespace std;
const int maxn=1e5+5;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int n,m1,m2,ans;
int res_in[maxn];
int res_out[maxn];
struct flight
{
int come;
int leave;
}in[maxn],out[maxn];
bool cmp(flight x,flight y)
{
return x.come<y.come;
}
void calc(flight* t,int m,int* res)
{
priority_queue <int,vector<int>,greater<int> > wait;
priority_queue <pii,vector<pii>,greater<pii> > have;
for(int i=1;i<=n;i++)
{
wait.push(i);
}
for(int i=1;i<=m;i++)
{
while(!have.empty() && have.top().first<=t[i].come)
{
wait.push(have.top().second);
have.pop();
}
if(wait.empty()) continue;
res[wait.top()]++;
have.push(make_pair(t[i].leave,wait.top()));
wait.pop();
}
for(int i=1;i<=n;i++)
{
res[i]=res[i-1]+res[i];
}
}
int main()
{
n=read(),m1=read(),m2=read();
for(int i=1;i<=m1;i++)
{
in[i].come=read();
in[i].leave=read();
}
for(int i=1;i<=m2;i++)
{
out[i].come=read();
out[i].leave=read();
}
sort(in+1,in+m1+1,cmp);
sort(out+1,out+m2+1,cmp);
calc(in,m1,res_in);
calc(out,m2,res_out);
for(int i=0;i<=n;i++)
{
ans=max(ans,res_in[i]+res_out[n-i]);
}
cout<<ans;
return 0;
}
T2 【CSP-S 2021】括号序列
这题好SB。。。。。
大家应该都能一眼鉴定位区间DP吧
但是转移属实是麻烦,第三位表示这个字符串的形状
dp[l][r][0] : ********(我真没骂人,打的就是星号)
dp[l][r][1] :(****)
dp[l][r][2] :()****
dp[l][r][3] :(里边只要合法就行)
dp[l][r][4] : ****()
dp[l][r][5] : **** () ****
然后就是要注意一下最后一种包含了第一种,第四种包含了第二种
然后就是暴力的转移一下就行力
最后因为题目要求是两边都是()所以第四种状态即为ans (dp[1][n][3])
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=510;
const int mod=1e9+7;
int n,k;
char str[maxn];
int dp[maxn][maxn][6];
bool cmp(int l,int r)
{
return ((str[l]=='(' || str[l]=='?') && (str[r]==')' || str[r]=='?'));
}
signed main()
{
scanf("%lld%lld",&n,&k); cin>>(str+1);
for(int i=1;i<=n;i++)
{
dp[i][i-1][0]=1; //初始化是为了方便转移
}
for(int len=1;len<=n;len++)
{
for(int l=1;l<=n-len+1;l++)
{
int r=l+len-1;
if(len<=k) dp[l][r][0]=dp[l][r-1][0]*(str[r]=='?' || str[r]=='*'); //从这行开始就很一目了然
if(len>=2)
{
if(cmp(l,r)) 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])%mod;
for(int i=l;i<r;i++)
{
dp[l][r][2]=(dp[l][r][2]+dp[l][i][3]*dp[i+1][r][0])%mod;
dp[l][r][3]=(dp[l][r][3]+(dp[l][i][2]+dp[l][i][3])*dp[i+1][r][1])%mod;
dp[l][r][4]=(dp[l][r][4]+(dp[l][i][4]+dp[l][i][5])*dp[i+1][r][1])%mod;
dp[l][r][5]=(dp[l][r][5]+dp[l][i][4]*dp[i+1][r][0])%mod;
}
}
dp[l][r][5]=(dp[l][r][5]+dp[l][r][0])%mod;
dp[l][r][3]=(dp[l][r][3]+dp[l][r][1])%mod;
}
}
cout<<dp[1][n][3];
return 0;
}
T3 【CSP-S 2021】回文
借鉴了@Eafoo的思路:先针对第一个出去的元素进行讨论
先讨论从左出去的情况(从右出去也同理),因为这样字典序肯定比先从右出去小
我们先找到第一个出去的元素的同素异形体(bushi),然后就把序列分成了两段
用两个双端队列维护,数组ans1表示第一个这样的字符出去的方向,ans2表示和他一样的那个字符
那么假设这个元素是i个出去的,另一个就是倒数第i个,所以ans2要倒序输出
剩下的就是按回文还有字典序的顺序什么的模拟判断了(突然感觉压行也挺不错滴)
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<deque>
#include<algorithm>
using namespace std;
const int maxn=1e6+5;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int t,n,cnt_1,cnt_2;
int a[maxn];
char ans1[maxn],ans2[maxn];
deque <int> l,r;
bool check()
{
while(!l.empty() || !r.empty())
{
bool L=!l.empty(),R=!r.empty();
bool LL=l.size()>1,RR=r.size()>1;
char lc,rc;
if(LL && l.front()==l.back()) l.pop_front(),l.pop_back(),lc='L',rc='L';
else if(L && R && l.front()==r.back()) l.pop_front(),r.pop_back(),lc='L',rc='R';
else if(L && R && r.front()==l.back()) r.pop_front(),l.pop_back(),lc='R',rc='L';
else if(RR && r.front()==r.back()) r.pop_front(),r.pop_back(),lc='R',rc='R';
else break;
ans1[++cnt_1]=lc,ans2[++cnt_2]=rc;
}
if(l.empty() && r.empty())
{
for(int i=1;i<=cnt_1;i++)
{
cout<<ans1[i];
}
for(int i=cnt_2;i>=1;i--)
{
cout<<ans2[i];
}
cout<<endl;
return true;
}
return false;
}
void work()
{
n=2*read();int p;
l.clear(),r.clear();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=2;i<=n;i++) if(a[i]==a[1]){ p=i; break; }
for(int i=2;i<p;i++) l.push_back(a[i]);
for(int i=n;i>=p+1;i--) r.push_back(a[i]);
ans1[cnt_1=1]='L',ans2[cnt_2=1]='L';
if(check()) return ;
l.clear(),r.clear();
for(int i=1;i<n;i++) if(a[i]==a[n]){ p=i; break; };
for(int i=1;i<p;i++) l.push_back(a[i]);
for(int i=n-1;i>=p+1;i--) r.push_back(a[i]);
ans1[cnt_1=1]='R',ans2[cnt_2=1]='L';
if(check()) return;
cout<<-1<<endl;
}
int main()
{
t=read();while(t--) work();
return 0;
}
T4 【CSP-S 2021】交通规划
很明显,这题不可做,考场上骗骗分(k<=2)然后自求多福吧