海亮寄 7.5
前言
业精于勤荒于嬉,行成于思毁于随
正文(模拟赛)
卦象:吉
模拟赛原题来源:KOI 2024
感受:是 IOI 赛制,\(3\) 个小时 \(7\) 道题,分数 \(431/700\)。感觉整体问题不大,当然这场模拟赛也暴露了若干短板。开始两个小时写前三道题目,顺序 \(1 \to 3 \to 2\),后面预测自己切不了 T4 且 T5 是原题,所以开 T6。发现 T6 诈骗,15 min 切掉后剩余时间观测 T7。然而 T7 于我而言是不可做题,故回去打 T4 暴力分数。短板也很明显,相对于一些代码细节的问题处理花费的时间更长(又称代码能力弱),并且打字速度并不是很快,所以还需要强化练习
T1
人口普查题,过
T2
人口普查题 \(\times 2\),讲题时老师把这道题目升华到一个匪夷所思的高度,根本没有想到这道题目居然能和 flood-fill 扯上关系
T3
图论建模题
一开始尝试直接贪心模拟过程,最后发现不需要输出方案。于是乎考虑图论建模,观测到对于同一个连通块内,如果存在多组链式结构则无解,否则答案等于点数 + 连通块数 - 自环数
T4
神秘 DP 题
赛场上打了 \(31\) pts 的暴力,后来发现确实有一些引导作用。比如维护 L[x][y] 和 R[x][y] 和 f[x][y] 等。进一步地,我们考虑如何去除枚举这一复杂度
容易想到预处理,简单分讨一波
-
V 字无关(即奇偶性不同)
直接查询奇数组和偶数组的 \(\max \{ f \}\) 即可
-
V 字包含
维护
B[i][j]被 \((i,j)\) 所在 V 字包含的最大 \(f\) 值转移方程形如:
B[i][j]=max({B[i-1][j-1],B[i-1][j],B[i-1][j+1],f[i][j]});答案是好贡献的
-
V 字相交
假设先染色靠左的 V 字,发现靠左 V 字的右翼会将网格图分割,依照题意,靠右 V 字是无法染色靠左 V 字右翼以上的部分的
形式化地说,靠右 V 字只会贡献 \(x+y>k\) 的部分,\(k\) 是与选中的靠左 V 字有关的一个常数
所以维护
RV[x][y]表示 \((x,y)\) 先向右下再向右上的最大贡献转移方程形如:
RV[i][j]=max(R[i][j],RV[i+1][j+1]+1);然后我们发现,我们需要对上述直线 \(x+y=k\) 维护最大的
RV值,可以记作pRV答案的来源形如确定一个 \(k\),在 \(x+y \le k\) 的部分选取一个最大的
f值,然后在 \(x+y > k\) 的部分选取一个最大的pRV值前后缀最大值处理即可,时间复杂度很有保障
对于先选右侧,再选左侧的部分是完全对称的,可以类似地定义
LV与pLV,不多赘述
综上所述,我们只需要在对上述三种情况取 \(\max\) 就可以获得答案
预处理部分较多,代码实现难度中等偏上
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e3+5;
int n,m,L[N][N],R[N][N],f[N][N],B[N][N];char a[N][N];
int LV[N][N],RV[N][N],pLV[N<<1],pRV[N<<1],fLV[N<<1],fRV[N<<1];
inline void chkmx(int &x,int y){x=max(x,y);return;}
inline void chkmn(int &x,int y){x=min(x,y);return;}
inline void workl(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
L[i][j]=(a[i][j]=='0'?0:L[i-1][j-1]+1);
return;
}
inline void workr(){
for(int i=1;i<=n;i++)
for(int j=m;j>=1;j--)
R[i][j]=(a[i][j]=='0'?0:R[i-1][j+1]+1);
return;
}
inline void workf(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j]=(a[i][j]=='0'?0:L[i][j]+R[i][j]-1);
return;
}
inline void workb(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
B[i][j]=max({B[i-1][j-1],B[i-1][j],B[i-1][j+1],f[i][j]});
return;
}
inline void worklv(){
for(int i=n;i>=1;i--)
for(int j=1;j<=m;j++)
if(a[i][j]=='1')
LV[i][j]=max(L[i][j],LV[i+1][j-1]+1);
return;
}
inline void workrv(){
for(int i=n;i>=1;i--)
for(int j=1;j<=m;j++)
if(a[i][j]=='1')
RV[i][j]=max(R[i][j],RV[i+1][j+1]+1);
return;
}
inline void work(){
for(int i=n;i>=1;i--)
for(int j=1;j<=m;j++){
if(a[i][j]=='0')continue;
chkmx(pLV[j+n-i+1],LV[i][j]);
chkmx(pRV[i+j],RV[i][j]);
chkmx(fLV[j+n-i+1],f[i][j]);
chkmx(fRV[i+j],f[i][j]);
}
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>(a[i]+1);
workl(),workr(),workf(),workb();
worklv(),workrv(),work();
for(int i=1;i<=n+m;i++)chkmx(pLV[i],pLV[i-1]);
for(int i=n+m;i>=1;i--)chkmx(pRV[i],pRV[i+1]);
for(int i=1;i<=n+m;i++)chkmx(fRV[i],fRV[i-1]);
for(int i=n+m;i>=1;i--)chkmx(fLV[i],fLV[i+1]);
int odd=0,even=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if((i+j)&1)chkmx(odd,f[i][j]);
else chkmx(even,f[i][j]);
}
int ans=odd+even;
for(int i=1;i<=n+m-1;i++){
chkmx(ans,pLV[i]+fLV[i+1]);
chkmx(ans,fRV[i]+pRV[i+1]);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
chkmx(ans,f[i][j]+B[i-1][j]);
cout<<ans<<'\n';
return 0;
}
T5
原题,朴素贪心题
一个串显然是第一个出现 \(1\) 的位置所代表的后缀
贪心从高到低位贪,尽可能地满足条件,剩下的就是一些细节问题
建议结合代码食用
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+5;
int n;char s[N],ans[N];
inline void solve(){
cin>>n>>(s+1);
int p=1;
while(p<=n&&s[p]=='0')p++;
if(p>n){cout<<"0\n";return;}
int q=p;
while(q<=n&&s[q]=='1')q++;
if(q>n){
for(int i=p;i<n;i++)cout<<(char)(s[i]);
cout<<(p==1?"0\n":"1\n");
return;
}
int x=q;
while(x<=n&&s[x]=='0')x++;
int len=min(x-q,q-p);
int i=n,j=n-len;
while(j>=q-len){
ans[i-p+1]=(s[i]==s[j]?'0':'1');
i--,j--;
}
while(i>=p){ans[i-p+1]=s[i];i--;}
for(int i=1;i<=n-p+1;i++)cout<<(char)(ans[i]);
cout<<'\n';
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T;cin>>T;while(T--)solve();
return 0;
}
T6
诈骗题
显然每次查询我们只关注根节点周围的点的最小值。不妨用堆来存储当前根节点周围的所有点的编号及点权
贪心结论:将选出的子节点的所有儿子都直接加入到我们的堆里
感性理解一下,拿子节点中一个权值最小的点来代替该节点,和把所有子节点都直接链接到该点的父亲结点是等价的
因为每次必然是取最小的,再拿次小的替换,当我们把所有点都丢进去时,有堆可以保证不会先取大的数
由此,我们能明白我们的贪心是对的,接下来就是写代码了,就是优先队列的简单运用
T7
多种算法与数据结构的强大毒瘤综合题,目前还不会做
小结
讲题环节收获一般,没有听懂 T7。今天看看能不能学习一下,以后再说吧!
后记
世界孤立我任它奚落
完结撒花!

浙公网安备 33010602011771号