CCPC2024-girls D 优秀的拆分(LIS,数数)

CCPC2024-girls D 优秀的拆分(LIS,数数)

给一个排列 \(p\),求该排列元素不重复的 \(\max\{\text{LIS}+\text{LDS}\}\).

(具体地说是将该排列任意划分为两个子序列 A 与 B,求 \(\max\{\text{LIS(A)}+\text{LDS(B)}\}\),其中 \(\text{LIS(A)}\) 表示 A 的最长上升子序列的长度 ).

可以注意到对 \(p\) 任取一个 LIS 和一个 LDS,至多会有一个重合的元素.
因为答案上限是 LIS+LDS,所以答案只能为 LIS+LDS 或 LIS+LDS-1.

考虑什么情况下答案需要 -1: 若任取 LIS 和 LDS 必有一个重合的元素,则答案为 LIS+LDS-1.

去掉 LIS 再求 LDS 之类的想法并不好做. 可以把这个题转化成数数题():

求任取 LIS 和 LDS 的方案数 \(S=\text{SLIS}\times \text{SLDS}\)
其中 \(\text{SLIS}\) 表示 \(p\) 的 LIS 数量.

选定 \(a_k\),求在 \(a_k\) 处重合的一组 LIS 与 LDS 的组数 \(S_k=\text{LISg(k)}\times \text{LDSg(k)}\)
其中 \(\text{LIS(k)}\) 表示 \(p\) 的所有包含 \(a_k\) 的 LIS 数量.

\(S=\sum_{k=1}^{n}S_k\),则答案为 LIS+LDS-1,否则答案为 LIS+LDS.


因为需要做判等,所以想了一下 LIS 计数的量级. 如果把 \(n\) 排列 \(p\) 均分为 \(x\) 段,每一段都取连续的下降列,例如 321654987 这样,那么 LIS 数量应该是 \((\frac{n}{x})^x\). 求导,解得 \(x_0=\frac{n}{e}\),得上限 \(e^{\frac{n}{e}}\),还是指数级的. 因为题解没有提到,一开始以为量级是不会超过 i64 的()

虽然题解没有提到,目前看来可能得找个大质数当模数了.

如何做 LIS 计数?笨比 nqZ 昨天写了个俩 log 的做法.

实际上可以一 log,区间 f 和 g 的答案还是有结合律的.

转移:

Info operator + (Info a,Info b)
{
	return (Info){max(a.f,b.f), ((a.f==b.f)?((a.g+b.g)%mod):(a.f>b.f?a.g:b.g)),min(a.l,b.l),max(a.r,b.r)};
}

LIS 和 LDS 各做两次,处理出 \(f1\)\(g1\)\(f2\)\(g2\)\(f3\)\(g3\)\(f4\)\(g4\)

两个 sum 值分别是:

\[(\text{SLIS}=\sum_{f1(k)=\text{LIS}}g1_k) \times (\text{SLDS}=\sum_{f3(k)=\text{LDS}}g3_k)) \]

\[\sum_{f1_k+f2_k-1=\text{LIS}\ 且\ f3_k+f4_k-1=\text{LDS}} (g1_k\times g3_k)(g2_k\times g4_k) \]

struct Info
{
	i64 f=0,g=0,l=0,r=0;
	void apply(Tag t)
	{
		;
	}
};
Info operator + (Info a,Info b)
{
	return (Info){max(a.f,b.f), ((a.f==b.f)?((a.g+b.g)%mod):(a.f>b.f?a.g:b.g)),min(a.l,b.l),max(a.r,b.r)};
}
void R()
{
	int n;
	cin>>n;
	vector<int> p(n);
	for (int i=0;i<n;i++) cin>>p[i];
	SGT<Info,Tag> sgt1(n),sgt2(n),sgt3(n),sgt4(n);
	vector<int> f1(n),f2(n),f3(n),f4(n);
	vector<i64> g1(n),g2(n),g3(n),g4(n);
	int LIS=0,LDS=0;
	for (int i=0,L,R;i<n;i++)
	{
		Info U=sgt1.rangeQuery(0,p[i]-1);
		f1[i]=U.f+1;
		g1[i]=U.g;
		if (f1[i]==1) g1[i]=1;
		sgt1.modify(p[i]-1,{f1[i],g1[i],p[i]-1,p[i]-1});
		LIS=max(LIS,f1[i]);
	}
	i64 SLIS=0;
	for (int i=0;i<n;i++) if (f1[i]==LIS) SLIS=(SLIS+g1[i])%mod;
	// cout<<"LIS="<<LIS<<" SLIS="<<SLIS<<endl;
	for (int i=n-1,L,R;i>=0;i--)
	{
		Info U=sgt4.rangeQuery(0,p[i]-1);
		f4[i]=U.f+1;
		g4[i]=U.g;
		if (f4[i]==1) g4[i]=1;
		sgt4.modify(p[i]-1,{f4[i],g4[i],p[i]-1,p[i]-1});
	}
	for (int i=0,L,R;i<n;i++)
	{
		Info U=sgt3.rangeQuery(p[i],n);
		f3[i]=U.f+1;
		g3[i]=U.g;
		if (f3[i]==1) g3[i]=1;
		sgt3.modify(p[i]-1,{f3[i],g3[i],p[i]-1,p[i]-1});
		LDS=max(LDS,f3[i]);
	}
	for (int i=n-1,L,R;i>=0;i--)
	{
		Info U=sgt2.rangeQuery(p[i],n);
		f2[i]=U.f+1;
		g2[i]=U.g;
		if (f2[i]==1) g2[i]=1;
		sgt2.modify(p[i]-1,{f2[i],g2[i],p[i]-1,p[i]-1});
	}
	i64 SLDS=0;
	for (int i=0;i<n;i++) if (f3[i]==LDS) SLDS=(SLDS+g3[i])%mod;
	// cout<<"LDS="<<LDS<<" SLDS="<<SLDS<<endl;
	i64 sum=0;
	/*
	cout<<"f1-----------"<<endl; for (int i=0;i<n;i++) cout<<f1[i]<<" "; cout<<endl;
	cout<<"g1-----------"<<endl; for (int i=0;i<n;i++) cout<<g1[i]<<" "; cout<<endl;
	cout<<"f2-----------"<<endl; for (int i=0;i<n;i++) cout<<f2[i]<<" "; cout<<endl;
	cout<<"g2-----------"<<endl; for (int i=0;i<n;i++) cout<<g2[i]<<" "; cout<<endl;
	cout<<"f3-----------"<<endl; for (int i=0;i<n;i++) cout<<f3[i]<<" "; cout<<endl;
	cout<<"g3-----------"<<endl; for (int i=0;i<n;i++) cout<<g3[i]<<" "; cout<<endl;
	cout<<"f4-----------"<<endl; for (int i=0;i<n;i++) cout<<f4[i]<<" "; cout<<endl;
	cout<<"g4-----------"<<endl; for (int i=0;i<n;i++) cout<<g4[i]<<" "; cout<<endl;
	*/
	for (int i=0;i<n;i++)
	{
		if (f1[i]+f2[i]-1==LIS&&f3[i]+f4[i]-1==LDS)
		{
			sum=(sum+g1[i]*g3[i]%mod*g2[i]%mod*g4[i]%mod)%mod;
		}
	}
	int ans=LIS+LDS;
	if (sum==SLIS*SLDS%mod) ans--;
	cout<<ans<<endl;
	return;
}
posted @ 2025-07-13 14:57  Akuto_urusu  阅读(40)  评论(0)    收藏  举报