CSP-S2021 题解
作者:互联网
T1.廊桥分配
\(n^2\)暴力:直接枚举分配过程按照题意模拟。
对于每架飞机,设当前区域内廊桥总数为\(x\),那么只有当\(x\)到达一定值时,它才有贡献
考虑对于每个可能的\(x\),求出来它可以使得多少飞机有贡献,那么只要前缀和一下再\(O(n)\)扫一遍就可以求得答案
思考怎么求每个\(x\)的贡献值,注意到体重说有一个先到先得的规定,也就是说当廊桥数量固定后,情况是唯一的,不存在方案优不优的问题
从简单入手,每个飞机抽象称一条线段,如果只有应该廊桥,那么肯定是一开始不交的线段有贡献
可以用set
维护,每次相当于从前往后扫,不交的线段数量就是当前的\(x\)的贡献数,如果到头了从头开始,\(x\)要加一
由于每次一定会从集合里删去元素,所以算上lower_bound
的复杂度上界是\(n\log n\)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=100050;
int a1[N],a2[N],b1[N],b2[N];
int lsh[4*N],n,m1,m2,tot,cnt;
int ans1[N],ans2[N];
set <pair<int,int> >s;
signed main()
{
//freopen("airport.in","r",stdin);
//freopen("airport.out","w",stdout);
cin>>n>>m1>>m2;
for(int i=1;i<=m1;i++)scanf("%lld%lld",&a1[i],&a2[i]);
for(int i=1;i<=m2;i++)scanf("%lld%lld",&b1[i],&b2[i]);
for(int i=1;i<=m1;i++)lsh[i]=a1[i],lsh[m1+i]=a2[i];
tot=m1*2;
for(int i=1;i<=m2;i++)lsh[tot+i]=b1[i],lsh[tot+m2+i]=b2[i];
tot+=m2*2;sort(lsh+1,lsh+1+tot);
cnt=unique(lsh+1,lsh+1+tot)-lsh-1;
for(int i=1;i<=m1;i++)
{
a1[i]=lower_bound(lsh+1,lsh+1+cnt,a1[i])-lsh;
a2[i]=lower_bound(lsh+1,lsh+1+cnt,a2[i])-lsh;
}
for(int i=1;i<=m2;i++)
{
b1[i]=lower_bound(lsh+1,lsh+1+cnt,b1[i])-lsh;
b2[i]=lower_bound(lsh+1,lsh+1+cnt,b2[i])-lsh;
}
for(int i=1;i<=m1;i++)s.insert(make_pair(a1[i],a2[i]));
int ma=1e9,num=0;
while(s.size())
{
auto ga=s.lower_bound(make_pair(ma,0));
if(ga==s.end())
{
num++;ga=s.begin();
ma=(*ga).second;s.erase(ga);
ans1[num]++;continue;
}
ans1[num]++;ma=(*ga).second;
s.erase(ga);
}
for(int i=1;i<=m2;i++)s.insert(make_pair(b1[i],b2[i]));
ma=1e9;num=0;
while(s.size())
{
auto ga=s.lower_bound(make_pair(ma,0));
if(ga==s.end())
{
num++;ga=s.begin();
ma=(*ga).second;s.erase(ga);
ans2[num]++;continue;
}
ans2[num]++;ma=(*ga).second;
s.erase(ga);
}
for(int i=1;i<=n;i++)
{
ans1[i]=ans1[i-1]+ans1[i];
ans2[i]=ans2[i-1]+ans2[i];
}
int an=0;
for(int i=0;i<=n;i++)an=max(an,ans1[i]+ans2[n-i]);
cout<<an<<endl;
return 0;
}
因为并不需要在值域上开数组,所以离散化是不必要的
T2.括号序列
考场上直接弃了,这也反应了dp能力确实有待提高
本来是思考的dp,但想的一直是线性dp,最终没能仔细理解题目,暴力也不大会
考虑区间dp,设计状态是\(f_{l,r}\)和\(g_{l,r}\)分别表示\(l,r\)这段区间合法,最左边和最右边的括号匹配/不匹配的方案数
为啥要设计这个状态是因为避免重复,注意这里匹配不匹配不是指是不是左右括号(否则直接不合法跳过),而是指最左边和最右边的是否能配成一对,还是他们分别和中间的什么配成了对
预处理\(p_{l,r}\)表示一个区间内是否全是\(*\)或者\(?\),一个布尔数组,转移时要用
转移的主要思路就是看拼成的合法序列是他说的哪一种,对应不同转移
边界特判\(len=2\),两端不合法直接continue
,然后讨论转移方程
先是匹配的\(f\)数组
当最终\((S)\)的时候,中间一定都是星号,就有
一定注意中间长度不能超过\(k\)
最终\((A)\)也类似,中间只要合法即可
对于\((AS)\)和\((SA)\)的转移是类似的,枚举连续一段星号的个数\(O(n)\)转移
\[(SA):f_{l,r}+=\sum_{i=1}^k (f_{l+i+1,r-1}+g_{l+i+1,r-1})[p_{l+1,l+i}] \]\[(AS):f_{l,r}+=\sum_{i=1}^k (f_{l+1,r-i-1}+g_{l+1,r-i-1})[p_{r-i,r-1}] \]枚举的时候注意\(i\)的上界注意不要超,应该和右端点取min
剩下的\(ASB\)和\(AB\)就是另一种情况\(g\)了,转移要枚举中间\(S\)的长度,同时避免重复要钦定后面一定是匹配的
这里定义\(p_{x,x-1}\)也是1,便于转移
发现最后一个暴力转移是\(n^2\),直接写应该是65分,考虑优化
发现最后在累加就是在统计一个前面有若干个星号,后面是一个匹配的序列的方案数,那么可以直接把这个类似后缀和的数组维护出来
设\(dp_{l,r}\)表示上述数组,那么可以按照转移\(SA\)类似的方法求出来
此外所有的f方案都应该计入对应的dp方案之中,所以还要加上(因为上面的式子没有算)
那么最后转移直接把对应\(j\)的枚举换成dp数组就行了,总复杂度\(n^3\)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=505;
const int mod=1e9+7;
int f[N][N],g[N][N],n,kk,ga[N][N];
bool p[N][N];char c[N];
signed main()
{
cin>>n>>kk;scanf("%s",c+1);
for(int i=1;i<=n;i++)
{
p[i][i-1]=1;
for(int j=i;j<=n;j++)
{
bool fg=1;
for(int k=i;k<=j;k++)if(c[k]!='?'&&c[k]!='*')
{fg=0;break;}
p[i][j]=fg;
}
}
for(int len=2;len<=n;len++)
{
for(int l=1;l<=n-len+1;l++)
{
int r=l+len-1;
if((c[l]!='('&&c[l]!='?')||(c[r]!=')'&&c[r]!='?'))continue;
if(len==2){f[l][r]=ga[l][r]=1;continue;}
if(r-l-1<=kk)f[l][r]=(f[l][r]+p[l+1][r-1])%mod;
f[l][r]=(f[l][r]+f[l+1][r-1]+g[l+1][r-1])%mod;
for(int i=1;i<=kk&&l+1+i<=r-1;i++)if(p[l+1][l+i])f[l][r]=(f[l][r]+f[l+i+1][r-1]+g[l+i+1][r-1])%mod;
for(int i=1;i<=kk&&r-i-1>=l+1;i++)if(p[r-i][r-1])f[l][r]=(f[l][r]+f[l+1][r-i-1]+g[l+1][r-i-1])%mod;
for(int i=l+1;i<r;i++)g[l][r]=(g[l][r]+(f[l][i]+g[l][i])*ga[i+1][r]%mod)%mod;
}
for(int l=1;l<=n-len+1;l++)
{
int r=l+len-1;if(c[r]!=')'&&c[r]!='?')continue;
ga[l][r]=f[l][r];
for(int i=1;i<=kk&&l+i<=r;i++)if(p[l][l+i-1])ga[l][r]=(ga[l][r]+f[l+i][r])%mod;
}
}
cout<<(f[1][n]+g[1][n])%mod<<endl;
return 0;
}
T3.回文
数据范围启发正解的时间复杂度应该就是\(O(n)\),带\(log\)的可能性不是很大
观察样例会发现,已选数字剩余位置一定是连续的,因此可以以这个进行贪心
反证如果不连续,那么两个位置一定会挡住一些数字,使得最后一定形不成回文
那么维护几个指针开始扫,优先选左边保证字典序,当选出\(n\)个数之后利用回文性质反推出剩下\(n\)个操作都是什么
一个细节是不能选择重复数字,当已经选择的和剩余位置挨上需要特判一下,例子可以看大样例98组两个一起的17
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=500050;
int n,a[2*N],b[2*N],tot;
int ans[2*N];vector <int> p[2*N];
inline void clear()
{
for(int i=1;i<=2*n;i++)p[i].clear(),a[i]=b[i]=ans[i]=0;
}
inline void gan(int l,int r)
{
for(int i=1;i<=n;i++)
{
if(a[l]==b[n-i+1])l++,ans[++tot]=0;
else r--,ans[++tot]=1;
}
for(int i=1;i<=2*n;i++)
{
if(ans[i])putchar('R');
else putchar('L');
}
puts("");
}
signed main()
{
//freopen("palin.in","r",stdin);
//freopen("palin.out","w",stdout);
int t;cin>>t;
while(t--)
{
clear();scanf("%lld",&n);tot=0;
for(int i=1;i<=2*n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=2*n;i++)p[a[i]].push_back(i);
int ga=p[a[1]][0];if(ga==1)ga=p[a[1]][1];
int pl=ga-1,pr=ga+1;
int l=2,r=2*n;b[++tot]=a[1],ans[tot]=0;
for(int i=2;i<=n;i++)
{
bool ok=0;
int p1=p[a[l]][0];if(p1==l)p1=p[a[l]][1];
int p2=p[a[r]][0];if(p2==r)p2=p[a[r]][1];
if(p1==pl&&l<pl)b[++tot]=a[l++],ans[tot]=0,pl--,ok=1;
else if(p1==pr)b[++tot]=a[l++],ans[tot]=0,pr++,ok=1;
else if(p2==pl)b[++tot]=a[r--],ans[tot]=1,pl--,ok=1;
else if(p2==pr&&r>pr)b[++tot]=a[r--],ans[tot]=1,pr++,ok=1;
if(!ok){tot=0;break;}
}
if(tot==n){gan(l,r);continue;}
ga=p[a[2*n]][0];if(ga==2*n)ga=p[a[2*n]][1];
pl=ga-1,pr=ga+1;
l=1,r=2*n-1;b[++tot]=a[2*n],ans[tot]=1;
for(int i=2;i<=n;i++)
{
bool ok=0;
int p1=p[a[l]][0];if(p1==l)p1=p[a[l]][1];
int p2=p[a[r]][0];if(p2==r)p2=p[a[r]][1];
if(p1==pl&&l<pl)b[++tot]=a[l++],ans[tot]=0,pl--,ok=1;
else if(p1==pr)b[++tot]=a[l++],ans[tot]=0,pr++,ok=1;
else if(p2==pl)b[++tot]=a[r--],ans[tot]=1,pl--,ok=1;
else if(p2==pr&&r>pr)b[++tot]=a[r--],ans[tot]=1,pr++,ok=1;
if(!ok){tot=0;break;}
}
if(!tot){puts("-1");continue;}
gan(l,r);
}
return 0;
}
T4.交通规划
正解看不懂,写了个复杂对不太正确的网络流做法
黑白染色,格子里面的点直接连双向边,格子外面的白点从源点向其连边,连到最近的点,黑点由最近的点向他连边,连到汇点
发现这样答案就是最小割,即选择代价最少的边删掉把黑白割断,因此源汇点都是INF
理论是60-80,但是机房大佬都A了,人傻常数大卡不过去,洛谷数据最高95
#include <bits/stdc++.h>
using namespace std;
const int N=505;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
struct node{
int to,next,w;
}a[(int)2e6];
int head[N*N],mm=2;
inline void add(int x,int y,int w)
{
a[mm].to=y;a[mm].w=w;
a[mm].next=head[x];head[x]=mm++;
}
inline void gan(int x,int y,int z)
{
add(x,y,z);add(y,x,0);
}
int s,t,hd[N*N],d[N*N];
inline bool bfs()
{
memset(d,0x3f,sizeof(d));
memcpy(head,hd,sizeof(hd));
std::queue <int> q;
q.push(s);d[s]=0;
while(q.size())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=a[i].next)
{
if(!a[i].w)continue;
int y=a[i].to;
if(d[y]>d[x]+1)d[y]=d[x]+1,q.push(y);
}
if(x==t)return 1;
}
return 0;
}
int dfs(int x,int flow)
{
if(x==t)return flow;
int rest=flow,k;
for(int i=head[x];i;head[x]=i=a[i].next)
{
if(!a[i].w)continue;
int y=a[i].to;
if(d[y]==d[x]+1)
{
k=dfs(y,min(a[i].w,rest));
if(!k)d[y]=0;
else a[i].w-=k,a[i^1].w+=k,rest-=k;
}
if(!rest)break;
}
return flow-rest;
}
int n,m,T,w1[N][N],w2[N][N],mp[N][N],tot;
inline void clear()
{
mm=2;memset(head,0,sizeof(head));
}
signed main()
{
cin>>n>>m>>T;
for(int i=1;i<=n-1;i++)for(int j=1;j<=m;j++)w1[i][j]=read();
for(int i=1;i<=n;i++)for(int j=1;j<=m-1;j++)w2[i][j]=read();
s=++tot,t=++tot;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)mp[i][j]=++tot;
while(T--)
{
clear();int k=read(),cnt=tot;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
{
if(i>1)gan(mp[i][j],mp[i-1][j],w1[i-1][j]);
if(i<n)gan(mp[i][j],mp[i+1][j],w1[i][j]);
if(j>1)gan(mp[i][j],mp[i][j-1],w2[i][j-1]);
if(j<m)gan(mp[i][j],mp[i][j+1],w2[i][j]);
}
for(int i=1;i<=k;i++)
{
int w=read(),x=read(),op=read(),id;
if(x>=1&&x<=m)id=mp[1][x];
else if(x>m&&x<=m+n)id=mp[x-m][m];
else if(x>m+n&&x<=m*2+n)id=mp[n][m-(x-(m+n))+1];
else id=mp[n-(x-(2*m+n))+1][1];
if(op)gan(s,++cnt,1e9),gan(cnt,id,w);
else gan(++cnt,t,1e9),gan(id,cnt,w);
}
memcpy(hd,head,sizeof(hd));
int ans=0;
while(bfs())ans+=dfs(s,1e9);
printf("%d\n",ans);
}
return 0;
}
标签:int,题解,tot,++,long,ga,S2021,CSP,dp 来源: https://www.cnblogs.com/PMZG/p/15458535.html