CSP2021提高组复赛解析
前言
终于出成绩了我可以写博客辣,官方数据还没出就先放洛谷的题目链接了。
正题
T1-廊桥分配
https://www.luogu.com.cn/problem/P7913
题目大意
有m1m_1m1种一类飞机,m2m_2m2种二类飞机,每个飞机有一个占用时间的区间。要给两类飞机分配恰好nnn个廊桥。
如果对于一类飞机当它来到时如果有空的它这一类的廊桥就会分配给他。
求最多能容纳多少飞机。
1≤n≤105,1≤m1+m2≤1051\leq n\leq 10^5,1\leq m_1+m_2\leq 10^51≤n≤105,1≤m1+m2≤105
解题思路
因为飞机的策略就是能停就停,我们可以考虑贪心策略。
先考虑单类的飞机,假设分配的廊桥为kkk,当一辆飞机不能进入当且仅当现在kkk个廊桥已经被霸占了,此时如果需要停靠这俩飞机就需要新开一个廊桥。
我们可以设有无数个廊桥,然后我们优先分配编号小的廊桥,然后最后如果有kkk个廊桥时答案就是在1∼k1\sim k1∼k的廊桥排列的飞机数。
具体的做法对于两类各做一次,用一个优先队列维护现在所有被霸占的廊桥的恢复时间,然后用一个set维护现在空余的廊桥编号就可以了。
时间复杂度:O(nlogn)O(n\log n)O(nlogn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<queue>
#define mp(x,y) make_pair(x,y)
using namespace std;
const int N=1e5+10;
struct node{int l,r;
}a[N];
int n,m,f[N],g[N];set<int> s;
priority_queue<pair<int,int> > q;
bool cmp(node x,node y)
{return x.l<y.l;}
int main()
{int pm;scanf("%d%d%d",&n,&m,&pm);for(int i=1;i<=max(n,max(m,pm));i++)s.insert(i);for(int i=1;i<=m;i++)scanf("%d%d",&a[i].l,&a[i].r);sort(a+1,a+1+m,cmp);for(int i=1;i<=m;i++){while(!q.empty()&&-q.top().first<=a[i].l)s.insert(q.top().second),q.pop();int x=*s.begin();f[x]++;s.erase(x);q.push(mp(-a[i].r,x));}while(!q.empty())s.insert(q.top().second),q.pop();for(int i=1;i<=n;i++)f[i]+=f[i-1];m=pm;for(int i=1;i<=m;i++)scanf("%d%d",&a[i].l,&a[i].r);sort(a+1,a+1+m,cmp);for(int i=1;i<=m;i++){while(!q.empty()&&-q.top().first<=a[i].l)s.insert(q.top().second),q.pop();int x=*s.begin();g[x]++;s.erase(x);q.push(mp(-a[i].r,x));}while(!q.empty())s.insert(q.top().second),q.pop();for(int i=1;i<=n;i++)g[i]+=g[i-1];int ans=0;for(int i=0;i<=n;i++)ans=max(ans,f[i]+g[n-i]);printf("%d\n",ans);return 0;
}
T2-括号序列
https://www.luogu.com.cn/problem/P7914
题目大意
一个合格的括号序被定义为
然后给出带∗,(,),?*,(,),?∗,(,),?的字符串,然后求有多少种把???切换成(,),∗(,),*(,),∗的方案使得是一个合法的括号序。
1≤k≤n≤5001\leq k\leq n\leq 5001≤k≤n≤500
解题思路
开始考虑一个一个填发现不行。
然后这个复杂度考虑区间dpdpdp,设fl,rf_{l,r}fl,r表示区间l∼rl\sim rl∼r合法的方案。
然后考虑怎么转移,先维护一个sl,rs_{l,r}sl,r表示l∼rl\sim rl∼r是否能够凑成一个长度不超过kkk的全∗*∗序列。
对于第111种和第333种情况,先看下l,rl,rl,r是否能是′(′'('′(′和′)′')'′)′的形式,然后第一种情况就直接加sl+1,r−1+fl+1,r−1s_{l+1,r-1}+f_{l+1,r-1}sl+1,r−1+fl+1,r−1(对应S/AS/AS/A),第三种我们可以枚举k∈[l+1,r−1)k\in[l+1,r-1)k∈[l+1,r−1),然后转移fl+1,k×sk+1,r+sl+1,k×fk+1,rf_{l+1,k}\times s_{k+1,r}+s_{l+1,k}\times f_{k+1,r}fl+1,k×sk+1,r+sl+1,k×fk+1,r(对应了AS/SAAS/SAAS/SA)就好了。
第222种情况比较麻烦,我们需要枚举一个l≤L<R≤rl\leq L<R\leq rl≤L<R≤r然后中间填SSS,就是fl,L×fR,r×sL+1,R−1f_{l,L}\times f_{R,r}\times s_{L+1,R-1}fl,L×fR,r×sL+1,R−1。但是这个枚举比较慢,因为对于一个rrr,满足sl,r=1s_{l,r}=1sl,r=1的lll肯定是一个到rrr的区间,并且rrr向右移动时这个区间也向右移动,所以可以使用一个前缀和优化。
发现这样还是过不了样例,问题出在如果存在ASASAASASAASASA的情况,此时会被统计两次(∣ASA∣SA|ASA|SA∣ASA∣SA和AS∣ASA∣AS|ASA|AS∣ASA∣各一次),更多的同理,所以我们可以设gl,rg_{l,r}gl,r表示不带情况二时的合法方案,然后在后面转移就好了。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=510,P=1e9+7;
ll n,k,f[N][N],g[N][N],S[N][N];char s[N];
signed main()
{scanf("%lld%lld",&n,&k);scanf("%s",s+1);for(ll i=1;i<=n;i++){S[i][i-1]=1;for(ll j=i;j<=min(n,i+k-1);j++){S[i][j]=S[i][j-1]&(s[j]=='?'||s[j]=='*');if(!S[i][j])break;}}for(ll len=2;len<=n;len++)for(ll l=1;l<=n-len+1;l++){ll r=l+len-1;if((s[l]=='?'||s[l]=='(')&&(s[r]=='?'||s[r]==')')){(f[l][r]+=S[l+1][r-1]+f[l+1][r-1])%=P;for(ll k=l+1;k<r-1;k++)(f[l][r]+=f[l+1][k]*S[k+1][r-1]+f[k+1][r-1]*S[l+1][k])%=P;}ll sum=0,z=l;g[l][r]=f[l][r];for(ll k=l;k<r;k++){(sum+=f[l][k])%=P;while(!S[z+1][k])(sum-=f[l][z])%=P,z++;(f[l][r]+=g[k+1][r]*sum%P)%=P;}}printf("%lld\n",(f[1][n]+P)%P);return 0;
}
T3-回文
https://www.luogu.com.cn/problem/P7915
题目大意
有一个长度为2n2n2n的序列aaa,保证1∼n1\sim n1∼n都各出现了两次,你有两种操作
- 将aaa的开头添加到序列bbb的末尾并在aaa移除。
- 将aaa的末尾添加到序列bbb的末尾并在aaa移除。
一操作为LLL,二操作为RRR,要求使得最终bbb回文的情况下操作序列的字典序最小。
1≤T≤100,∑n≤5×1051\leq T\leq 100,\sum n\leq 5\times 10^51≤T≤100,∑n≤5×105
解题思路
显然第一个丢进bbb的肯定是第一个或者最后一个,我们先假设是第一个且数字为xxx,那么最后被丢进去的肯定是另一个xxx。
然后我们可以从这个xxx的位置开始作为一个区间,然后开始每次你丢进去的下一个数都必须在这个区间的左右,然后再用这个区间再扩展丢进去的数的另一个的对应位置。
但是这样暴力搜丢左边还是右边是2n2^n2n的显然不行,但是我们发现假设如果一个情况左右都能丢,在字典序最小的情况下我们肯定是先丢左边的,而此时丢了之后不会导致右边不能丢了,所以此时丢左边肯定是最优的。
所以其实这样搜是O(n)O(n)O(n)的,时间复杂度O(∑n)O(\sum n)O(∑n)。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int T,n,flag,a[N];char v[N];
void dfs(int d,int l,int r,int L,int R){if(l<L&&r>R){flag=1;for(int i=1;i<=2*n;i++)putchar(v[i]);putchar('\n');return;}if(d>n)return;if(L<=l&&(a[L]==a[l]&&L<l||a[L]==a[r]&&r<=R)){v[d]='L';if(a[L]==a[l]&&L<l)v[2*n-d+1]='L',dfs(d+1,l-1,r,L+1,R);else v[2*n-d+1]='R',dfs(d+1,l,r+1,L+1,R);}else if(r<=R&&(a[R]==a[l]&&L<=l||a[R]==a[r]&&r<R)){v[d]='R';if(a[R]==a[l]&&L<=l)v[2*n-d+1]='L',dfs(d+1,l-1,r,L,R-1);else v[2*n-d+1]='R',dfs(d+1,l,r+1,L,R-1);}return;
}
int main()
{scanf("%d",&T);while(T--){scanf("%d",&n);v[2*n]='L';flag=0;for(int i=1;i<=2*n;i++)scanf("%d",&a[i]);for(int i=2;i<=2*n;i++)if(a[i]==a[1]){v[1]='L';dfs(2,i-1,i+1,2,2*n);break;}if(flag)continue;for(int i=1;i<2*n;i++)if(a[i]==a[2*n]){v[1]='R';dfs(2,i-1,i+1,1,2*n-1);break;}if(flag)continue;puts("-1");}return 0;
}
T4-交通规划
https://www.luogu.com.cn/problem/P7916
题目大意
有一个nnn条水平线和mmm条垂直线交叉形成n×mn\times mn×m个格点的图,把所有的边按照顺时针排序如图。
每个格点之间有边权。
TTT次询问,每次给出线外的kkk个额外点的位置,颜色(黑白),和连接线内边界格点的边权。
要求给网格上的所有点染色,要求使得两端颜色不同的边权值和最小。
1≤T≤50,∑k≤50,2≤n,m≤5001\leq T\leq 50,\sum k\leq 50,2\leq n,m\leq 5001≤T≤50,∑k≤50,2≤n,m≤500。
解题思路
黑白染色求最小的权值其实就是为最小割,然后平面图最小割是可以转换成对偶图的最短路的。
显然的对于k=2k=2k=2的部分分就是直接求黑色额外点(如果颜色都相同显然答案为000)左右的对偶点在对偶图上的最短路。
对于kkk更大的情况我们具体分析一下对于下图的情况(为了好看用了红蓝代替黑白)
我们有两种割法(绿/黄)
发现其实可以写成四个点相互匹配的过程,由于产生交叉的肯定不优(通过改变匹配方式使得交叉部分消去),所以匹配的贡献可以直接写成最短路。
然后考虑如何找到优的匹配方案,我们可以把顺时针的和逆时针的匹配,因为如果顺顺-逆逆的匹配的话,肯定会产生交叉。(虽然这样匹配也可能产生交叉,但是因为权值不优所以不会影响答案)
然后就是一个二分图最大权值匹配的问题了,写个费用流就可以了。
虽然再利用交叉性质做环形区间dpdpdp也能过,但是我不会/kk
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<vector>
#include<queue>
#define ll long long
#define mp(x,y) make_pair(x,y)
using namespace std;
const ll N=510,M=N*N,K=110;
struct edge{ll to,next,w;
}a[M<<2];
struct node{ll w,p,t;
}q[K];
ll n,m,T,tot,ls[M],f[M],wz[N<<2];
vector<int> A,B;bool v[M];
priority_queue<pair<ll,ll> > qt;
ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}
struct Netflow{struct node{ll to,next,w,c;}a[K*K*2];ll tot=1,s=1,t=2,ans,ls[K],f[K],mf[K],pre[K];bool v[K];priority_queue<int> q;void clr(){tot=1;ans=0;memset(ls,0,sizeof(ls));return;}void addl(ll x,ll y,ll w,ll c){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;a[tot].c=c;a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;a[tot].c=-c;return;}bool SPFA(){memset(f,0x3f,sizeof(f));q.push(s);f[s]=0;v[s]=1;mf[s]=1e9;while(!q.empty()){ll x=q.top();q.pop();v[x]=0;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(a[i].w&&f[x]+a[i].c<f[y]){f[y]=f[x]+a[i].c;pre[y]=i;mf[y]=min(mf[x],a[i].w);if(!v[y])q.push(y),v[y]=1;}}}return (f[t]!=f[0]);}void Updata(){ll x=t;ans+=mf[t]*f[t];while(x!=s){a[pre[x]].w-=mf[t];a[pre[x]^1].w+=mf[t];x=a[pre[x]^1].to;}return;}ll GetAns(){while(SPFA())Updata();return ans;}
}Nt;
ll p(ll x,ll y)
{return x*(m+1)+y;}
void addl(ll x,ll y,ll w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=w;return;
}
void dij(ll s){memset(f,0x3f,sizeof(f));memset(v,0,sizeof(v));f[s]=0;qt.push(mp(0,s));while(!qt.empty()){ll x=qt.top().second;qt.pop();if(v[x])continue;v[x]=1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(f[x]+a[i].w<f[y]){f[y]=f[x]+a[i].w;qt.push(mp(-f[y],y));}}}return;
}
ll getp(ll x,ll f){if(x<=m)return p(0,x+f);if(x<=m+n)return p(x-m+f,m);if(x<=2*m+n)return p(n,m-(x-m-n+f));return p(n-(x-2*m-n+f),0);
}
bool cmp(node x,node y)
{return x.p<y.p;}
signed main()
{n=read();m=read();T=read();for(ll i=1;i<n;i++)for(ll j=1;j<=m;j++){ll w=read();addl(p(i,j-1),p(i,j),w);}for(ll i=1;i<=n;i++)for(ll j=1;j<m;j++){ll w=read();addl(p(i-1,j),p(i,j),w);}for(ll i=1;i<=m;i++)addl(p(0,i-1),p(0,i),0),wz[i]=tot;for(ll i=1;i<=n;i++)addl(p(i-1,m),p(i,m),0),wz[i+m]=tot;for(ll i=1;i<=m;i++)addl(p(n,m-i+1),p(n,m-i),0),wz[i+n+m]=tot;for(ll i=1;i<=n;i++)addl(p(n-i+1,0),p(n-i,0),0),wz[i+n+2*m]=tot;while(T--){ll k=read();Nt.clr();A.clear();B.clear();for(ll i=1;i<=k;i++){q[i].w=read();q[i].p=read();q[i].t=read();a[wz[q[i].p]].w=a[wz[q[i].p]-1].w=q[i].w;}sort(q+1,q+1+k,cmp);q[0]=q[k];q[k+1]=q[1];for(ll i=1;i<=k;i++)if(q[i].t==1&&q[i-1].t==0)A.push_back(getp(q[i].p,-1));for(ll i=1;i<=k;i++)if(q[i].t==1&&q[i+1].t==0)B.push_back(getp(q[i].p,0));for(ll i=0;i<A.size();i++){Nt.addl(1,3+i,1,0);dij(A[i]);for(ll j=0;j<B.size();j++)Nt.addl(3+i,3+A.size()+j,1,f[B[j]]);}for(ll i=0;i<B.size();i++)Nt.addl(3+A.size()+i,2,1,0);printf("%lld\n",Nt.GetAns());for(ll i=1;i<=k;i++)a[wz[q[i].p]].w=a[wz[q[i].p]-1].w=0;}return 0;
}
CSP2021提高组复赛解析相关推荐
- CSP2021提高组游记
CSP2021提高组游记 update:2021.10.25 艰难地开始写这篇博客 因为真的不想面对 但又不得不面对 或许--这就是人生吧 无数次的想要逃避 最终却又选择坚持 虽然知道这样我会很累 但 ...
- NOIP2010 提高组 复赛 translate 机器翻译
NOIP2010 提高组 复赛 translate 机器翻译 1.读题,很快弄明题意,单词不在内存中就查字典,统计查字典次数. 2.内存采用队列方式.统计进队列次数,即为查询次数. 3.程序很快编好, ...
- NOIP2013 提高组复赛解题报告
NOIP2013 提高组复赛 day1 day\;1 1002. 火柴排队 贪心+数据结构/归并排序 这个"相邻交换"让我联想到了NOIP2012_day1_task2_game那 ...
- 凡人升天传7——NOIP2010 提高组复赛题解
本蒟蒻在考试时最后一道直接报零*__*,悲痛欲绝,因此在这里著下本题解. 可恶的西西弗 虽然题目做的很垃圾,但在写题解中途不得不感叹除了最后一道,其他真是好水题呀!!! --------------- ...
- NOIP 2008 提高组 复赛 message 传字条
NOIP 2008 提高组 复赛 message 传字条 1.样例很快模拟成功,但感觉是凑出来的,没有章法. 2.深度优先遍历,但感觉容易超时. 3.动态规划?翻看他人代码,发现动态规划的写法,确实想 ...
- NOIP 提高组 复赛 历年 试题
NOIP 提高组 复赛 历年 试题 NOIP 2017 提高组 复赛 试题 https://wenku.baidu.com/view/70de9e29854769eae009581b6bd97f ...
- 洛谷-神奇的幻方-NOIP2015提高组复赛
题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,--,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将1写在第一行的中间. ...
- NOIP 2018提高组复赛C/C++试题及答案详解
NOIP 2018提高组历年真题 CCF NOIP2018 初赛提高组 C++语言试题 第 1 页,共9 页 第二十四届全国青少年信息学奥林匹克联赛初赛 提高组 C++语言试题 竞赛时间:2018 年 ...
- 2018 NOIP 提高组 复赛 day2
文章目录 T1 旅行 T1 分析 T2 填数游戏 T2 分析 T3 保卫王国 T3 分析 不得不说-noip的趋势越来越像是day1基础+思路稳定分数,基本确定你能在哪个获奖区,day2难度提高,给满 ...
最新文章
- arduino 控制无刷电机_智能控制轮椅来了,残疾人的福音!
- c++十六进制加法_C++中输出十六进制形式的字符串
- 01-----JavaScript简介
- 洛谷 [P1024]一元三次方程求解【二分答案】
- Silverlight Telerik控件学习:主题Theme切换
- vsftp匿名访问目录_怎么更改vsftp匿名用户的默认登录目录/var/ftp?
- Mysql管理之二进制日志文件的管理
- 白鹭引擎拉伸高度_摩托车界厚道王!129cc单缸风冷引擎,座高740mm+数字盘,6400起...
- Java基础(三)IO流和对象流
- 罗克韦尔AB PLC RSLogix数字量IO模块基本介绍
- 2020五四青年节 | 青年人在美团是怎样成长的?
- 生信分析之R语言常用R包一步下载
- 二叉树思想实现的计算器
- 这可能是人工智能领域覆盖最全的一份技术趋势报告
- 机器人会偷走你的饭碗吗——药剂师 vs. 巨型药品包装机
- 廖雪峰python视频教程-福利 | 廖雪峰官方Python教程,拿走不谢!
- 安装django-cors-headers
- 华为荣耀平板s8-701w刷机记
- 无线通信、广播电视、汽车电子行业都在用的矢量网络分析仪有什么功能
- HTML5智慧渔业WebGL可视化云平台
热门文章
- 读书笔记-互联网思维必读10本书之一《免费》
- java 按钮设置图片_Java中如何设置带图片按钮的大小
- vim 寄存器 操作_vim指令
- c语言程序设计黄保和第二章,C语言程序设计答案(黄保和编)第6章
- Linux链接文件包括,Linux操作系统——系统各目录有什么作用、以及文件链接过程...
- sqoop将hive导出到mysql_Sqoop hive导出到mysql[转]
- 详解python运行三种方式_详解python调用cmd命令三种方法
- git pull 覆盖本地_SVN与Git比较的优缺点差异
- leetcode518. 零钱兑换 II
- windows 禁用ipv6服务_在 Windows 7 中禁用IPv6协议/IPv6隧道