P9870题解

P9870 [NOIP2023] 双序列拓展

题目传送门

题解

T3 双序列拓展(expand)

考察:dp、人类智慧(

部分分启示正解。

  • 35ptsO(qnm)

将原题目转化为这样:两个指针分别指着两个序列,每次将任意至少一个指针向后移一个位置,并使每时每刻都满足两个指针所指的位置有单调的偏序关系。

所谓单调的偏序关系可以用第一个位置来判断,在下文我们只考虑 a<b 的情况。

我们考虑 dp:记 fi,j 表示 a1i 能否和 b1j 匹配,那么有转移 fi,j=[ai<bj]and(fi1,jorfi,j1orfi1,j1)。此时即可 O(qnm) 完成本题。

  • 70pts 的特殊性质

我们考虑构造 01 矩阵 c,其中 ci,j=[ai<bj],那么等价于我们要判断是否存在只走 1,只能向下、向右、向右下的路径从 (1,1)(n,m)

其实我们不会向右下走,因为可以证明不存在

1 0
0 1

这样的矩阵,但对正解没啥作用,所以我就没管了。

由于性质保证,我们发现最后一列与最后一行都是 1。接下来我们考虑 aan 外的最小值 aminbbm 外的最小值 bmin,如果 amin<bmin,那么 amin 所在的列也全是 1,于是我们可以递归下去;否则,一定有 bmin 所在的行全是 0

对于 amaxbmax 我们同样考虑,如果 bmax>amax 那么就继续递归,否则一定有 amax 所在的列全是 0,然后你发现,bmin 所在的行与 amax 所在的列把 (1,1) 围起来了!于是就直接返回 false。

实现的话预处理前缀 minmax 即可。

  • 100pts

其实 70pts 就是 100pts 的一半,物理意义的一半,我们寻找全局 amin 和全局 bmax,只要有一列或一行全是 0,那么就相当于分割了 (1,1)(n,m),否则做两次特殊性质即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int rd() {
	int s=0,m=0;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')m=1;ch=getchar();}
	while( isdigit(ch)) s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return m?-s:s;
}
int cc,n,m,q,c[500005],d[500005],a[500005],b[500005],pax[500005],pan[500005],sax[500005],san[500005],pbx[500005],pbn[500005],sbx[500005],sbn[500005];
bool calc1(int x,int y) {
	if(x==1||y==1) return 1;
	int ax=pax[x-1],an=pan[x-1],bx=pbx[y-1],bn=pbn[y-1];
	if(a[an]<b[bn]) return calc1(an,y);
	if(b[bx]>a[ax]) return calc1(x,bx);
	return 0;
}
bool calc2(int x,int y) {
	if(x==n||y==m) return 1;
	int ax=sax[x+1],an=san[x+1],bx=sbx[y+1],bn=sbn[y+1];
	if(a[an]<b[bn]) return calc2(an,y);
	if(b[bx]>a[ax]) return calc2(x,bx);
	return 0;
}
bool check() {
	if(a[1]>b[1]) {
		for(int i=1;i<=n;i++) a[i]*=-1;
		for(int i=1;i<=m;i++) b[i]*=-1;
	}
	pax[1]=1,pan[1]=1;
	for(int i=2;i<=n;i++)
		pax[i]=(a[pax[i-1]]>a[i]?pax[i-1]:i),
		pan[i]=(a[pan[i-1]]<a[i]?pan[i-1]:i);
	pbx[1]=1,pbn[1]=1;
	for(int i=2;i<=m;i++)
		pbx[i]=(b[pbx[i-1]]>b[i]?pbx[i-1]:i),
		pbn[i]=(b[pbn[i-1]]<b[i]?pbn[i-1]:i);
	sax[n]=n,san[n]=n;
	for(int i=n-1;i>=1;i--)
		sax[i]=(a[sax[i+1]]>a[i]?sax[i+1]:i),
		san[i]=(a[san[i+1]]<a[i]?san[i+1]:i);
	sbx[m]=m,sbn[m]=m;
	for(int i=m-1;i>=1;i--)
		sbx[i]=(b[sbx[i+1]]>b[i]?sbx[i+1]:i),
		sbn[i]=(b[sbn[i+1]]<b[i]?sbn[i+1]:i);
	int ax=pax[n],an=pan[n],bx=pbx[m],bn=pbn[m];
	if(a[an]>=b[bn]) return 0;
	if(b[bx]<=a[ax]) return 0;
	return calc1(an,bx)&calc2(an,bx);
}
signed main() {
	cin>>cc>>n>>m>>q;
	for(int i=1;i<=n;i++)
		a[i]=c[i]=rd();
	for(int i=1;i<=m;i++)
		b[i]=d[i]=rd();
	cout<<check();
	while(q--) {
		for(int i=1;i<=n;i++) a[i]=c[i];
		for(int i=1;i<=m;i++) b[i]=d[i];
		int k1=rd(),k2=rd();
		for(int i=1;i<=k1;i++) {
			int p=rd(),v=rd();
			a[p]=v;
		}
		for(int i=1;i<=k2;i++) {
			int p=rd(),v=rd();
			b[p]=v;
		}
		cout<<check();
	}
	return 0;
}

时间复杂度 O(q(n+m))

posted @   operator-  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示