Luogu P9870 NOIp2023 双序列拓展 题解 [ 紫 ] [ 动态规划 ] [ 分治 ] [ adhoc ]
双序列拓展:很妙的特殊性质类 dp 题,由部分分引导向正解。
题意简化
你可以把序列
观察
初看这题,只会打一个暴力匹配的暴搜。
然后忽然看到了这题的神秘性质,于是就把这条性质拆分成了两部分:
是序列 唯一的一个最小值, 是序列 唯一的一个最大值。
第一条性质
我们观察每个数列的第一项有什么特别的,容易发现,无论后面的数怎么复制,两个数列的第一个数都一定是匹配上的。也就是说,
于是我们根据每个数列的第一个元素判断到底是
由此我们可以设计 dp:
转移显然有三种:
第二条性质
我们观察到这个方程很像方格取数,把他转化到方格上来。(这是本题思维难度最大的地方,要有抽象问题的能力)
一个方格
这里借一张洛谷题解的图:
发现我们很快能标出所有能被走的点,并且对于第二条性质,如果这个序列要存在,第
为啥呢?因为第
那么接下来怎么求解呢?我们可以采用分支的思路,将
正解
下面肯定要往最大值最小值上面去想了。假设当前
因为行列交叉形成一个十字架,所以我们递归求解左上部分与右下部份的答案即可,两者都能有解最终才有解。
如下图:
具体实现上,我们分别维护
因为每次分治最多只会修改一个数,那么每行每列总共一定不会超过
代码
参考了很多洛谷题解的细节与马蜂,确实是最好写、最直观的一种维护方式了。
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pi;
int n,m,a[500005],b[500005],pa[500005],va[500005],pb[500005],vb[500005];
struct node{
int mx,mn;
}prea[500005],preb[500005],sufa[500005],sufb[500005];
void init()
{
prea[1]={1,1};
for(int i=2;i<=n;i++)
{
prea[i]=prea[i-1];
if(a[prea[i].mx]<a[i])prea[i].mx=i;
if(a[prea[i].mn]>a[i])prea[i].mn=i;
}
preb[1]={1,1};
for(int i=2;i<=m;i++)
{
preb[i]=preb[i-1];
if(b[preb[i].mx]<b[i])preb[i].mx=i;
if(b[preb[i].mn]>b[i])preb[i].mn=i;
}
sufa[n]={n,n};
for(int i=n-1;i>=1;i--)
{
sufa[i]=sufa[i+1];
if(a[sufa[i].mx]<a[i])sufa[i].mx=i;
if(a[sufa[i].mn]>a[i])sufa[i].mn=i;
}
sufb[m]={m,m};
for(int i=m-1;i>=1;i--)
{
sufb[i]=sufb[i+1];
if(b[sufb[i].mx]<b[i])sufb[i].mx=i;
if(b[sufb[i].mn]>b[i])sufb[i].mn=i;
}
}
bool checkl(int x,int y)
{
if(x==1||y==1)return 1;
node nx=prea[x-1],ny=preb[y-1];
if(a[nx.mn]<b[ny.mn])return checkl(nx.mn,y);
if(a[nx.mx]<b[ny.mx])return checkl(x,ny.mx);
return 0;
}
bool checkr(int x,int y)
{
if(x==n||y==m)return 1;
node nx=sufa[x+1],ny=sufb[y+1];
if(a[nx.mn]<b[ny.mn])return checkr(nx.mn,y);
if(a[nx.mx]<b[ny.mx])return checkr(x,ny.mx);
return 0;
}
void solve()
{
bool swp=0;
if(a[1]==b[1])
{
cout<<0;
return;
}
if(a[1]>b[1])
{
swap(a,b);
swap(n,m);
swp=1;
}
init();
node x=prea[n],y=preb[m];
if(a[x.mn]>=b[y.mn]||a[x.mx]>=b[y.mx])
{
cout<<0;
if(swp)
{
swap(a,b);
swap(n,m);
}
return;
}
cout<<(checkl(x.mn,y.mx)&&checkr(x.mn,y.mx));
if(swp)
{
swap(a,b);
swap(n,m);
}
}
int main()
{
//freopen("expand.in","r",stdin);
//freopen("expand.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int c,q;
cin>>c>>n>>m>>q;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++)cin>>b[i];
solve();
while(q--)
{
int kx,ky;
cin>>kx>>ky;
for(int i=1;i<=kx;i++)
{
cin>>pa[i]>>va[i];
swap(a[pa[i]],va[i]);
}
for(int i=1;i<=ky;i++)
{
cin>>pb[i]>>vb[i];
swap(b[pb[i]],vb[i]);
}
solve();
for(int i=1;i<=kx;i++)
{
swap(a[pa[i]],va[i]);
}
for(int i=1;i<=ky;i++)
{
swap(b[pb[i]],vb[i]);
}
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战