// 鼠标点击特效 //

CF2003E解题报告

题目描述(来自谷歌翻译)

Turtle 为您提供 \(m\) 个区间 \([l_1, r_1], [l_2, r_2], \ldots, [l_m, r_m]\) 。他认为,如果每个区间 ( \(l_i \le k_i< r_i\) ) 都存在一个整数 \(k_i\) ,则排列 \(p\) 是有趣的,并且如果他让从 \(1\)\(m\) 的每个整数 \(i\) 都存在 \(a_i = \max\limits_{j = l_i}^{k_i} p_j, b_i = \min\limits_{j = k_i + 1}^{r_i} p_j\) ,则以下条件成立:

\[\max_{i = 1}^m a_i < \min_{i = 1}^m b_i \]

Turtle 希望您计算长度为 \(n\) 的所有有趣排列的最大逆序对数,或者告诉他是否存在有趣的排列。

思路点拨(E1)

此部分具有特殊性质,保证 \(r_i<l_{i+1}\)

通过观察样例答案的形态,发现:答案是将序列划分为两个部分,满足第一个部分权值的最大值小于第二个部分权值的最小值,且每一个部分的权值都是降序排列的。

简单想一下这个为什么是对的?在一个区间 \([l_i,r_i]\) 中,假设选出了划分点 \(k_i\) 满足 \([l_i,k_i]\) 作为第一部分,\((k_i,r_i]\) 作为第二部分。如果每一个部分不是降序排列的,不可以最大化逆序对数。对于不在区间中的元素(就是可以自由选择第一,第二部分的元素),考虑这样的第一个元素,从最大化逆序对数量的角度,选择第一部分最小值,第二部分最小值之外的权值显然不是最优的。那么归纳下去,对于全部不在区间中的元素都是如此。

所以我们可以考虑动态规划,定义 \(f_{i,j}\) 表示目前考虑到第 \(i\) 个区间(如果一个元素不在区间中,认为单独构成了一个区间),选择了 \(j\) 个第二部分元素的最大逆序对数(不考虑每一个部分内部的逆序对数量)。转移是简单的:

  • 如果一个区间是可以自由选择的元素:\(f_{i,j}=f_{i-1,j}+j\) ,表示选择第一部分。\(f_{i,j}=f_{i-1,j-1}\) ,表示选择第二部分。

  • 如果一个区间不可以自由选择元素,则枚举一个划分点,将区间划分为两个部分,第一,第二部分元素数量分别为 \(P,Q(\min\{P,Q\}>0)\) 。转移就是:\(f_{i,j}=f_{i-1,j-Q}+P\times (j-Q)\)

时间复杂度 \(O(n^2)\) ,可以通过此部分。

思路点拨(E2)

问题在于重叠部分的处理。

考虑两个重叠的区间 \([l_1,r_1],[l_2,r_2]\) ,如果 \(l_1 \leq l_2 \leq r_1 \leq r_2\) ,则:

  • \([l_1,l_2]\) 部分全部得填入第一部分。

  • \([r_1,r_2]\) 部分全部得填入第二部分。

  • \((l_2,r_1)\) 可以任意填入。

这可以理解为,我们将两个相交的区间合并为了一个新区间。

继续考虑两个包含的区间 \([l_1,r_1],[l_2,r_2]\) ,如果 \(l_1 \leq l_2 \leq r_2\leq r_1\) ,则:

  • \([l_1,l_2]\) 部分全部得填入第一部分。

  • \([r_2,r_1]\) 部分全部得填入第二部分。

  • \((l_2,r_2)\) 可以任意填入。

这依然可以理解为,我们将两个包含的区间合并为了一个新区间。

最终,我们可以通过上述两种合并方式,将区间转化为一些不相交的区间,回到了E1。

时间复杂度 \(O(n^2)\) 。但是因为我实现比较懒,写的 \(O(n^2 \log n)\) ,也过了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int MAXN=5e3+5,inf=1e18;
int T,n,m;
pair<int,int> a[MAXN],p[MAXN*MAXN/2];
int b[MAXN],c[MAXN],sum[MAXN],op[MAXN];
int dp[MAXN][MAXN];
int f(int n){
	return n*(n-1)/2;
}
signed main(){
	T=read();
	while(T--){
		n=read(),m=read();
		for(int i=1;i<=m;i++)
			a[i].first=read(),a[i].second=read();
		bool flag=1;
		memset(op,-1,sizeof(op));
		memset(b,0,sizeof(b)),memset(c,0,sizeof(c));
		for(int i=1;i<=m;i++){
			for(int j=i;j<=m;j++){
				int L=max(a[i].first,a[j].first),R=min(a[i].second,a[j].second);
				int l=min(a[i].first,a[j].first),r=max(a[i].second,a[j].second);
				if(L<=R){
					if(L==R) flag=0;
					else{ 
						b[l]++,b[L+1]--;
						c[R]++,c[r+1]--;
					}
				}
			}
		}
		for(int i=1;i<=n;i++){
			b[i]+=b[i-1],c[i]+=c[i-1];
			if(b[i]&&c[i]) flag=0;
			if(b[i]) op[i]=0;
			else if(c[i]) op[i]=1;
			sum[i]=sum[i-1]+(op[i]==-1);
		}
		if(!flag){
			cout<<-1<<'\n';
			continue;
		}
		
		int tot=0;
		for(int i=1;i<=m;i++){
			for(int j=i;j<=m;j++){
				int L=max(a[i].first,a[j].first),R=min(a[i].second,a[j].second);
				if(L+1<R&&sum[R-1]-sum[L]==R-L-1) p[++tot]=make_pair(L+1,R-1);
			}
		}
		sort(p+1,p+tot+1);
		m=tot;
		for(int i=1;i<=m;i++)
			for(int j=p[i-1].second+1;j<p[i].first;j++)
				p[++tot]=make_pair(j,j);
		for(int i=p[m].second+1;i<=n;i++)	
			p[++tot]=make_pair(i,i);
		sort(p+1,p+tot+1);
		m=tot,tot=0;
		for(int i=1;i<=m;i++)
			if(p[i]!=p[i-1]) p[++tot]=p[i];

		for(int i=0;i<=n;i++)
			for(int j=0;j<=n;j++) dp[i][j]=-inf;
		dp[0][0]=0;
		for(int i=1;i<=tot;i++){
			if(p[i].first==p[i].second){
				int id=p[i].first;
				for(int j=0;j<=n;j++){
					if(op[id]!=1){
						dp[i][j]=max(dp[i][j],dp[i-1][j]+j);
					}
					if(op[id]!=0){
						if(j>0) dp[i][j]=max(dp[i][j],dp[i-1][j-1]);
					}
				}
			}
			else{
				for(int mid=p[i].first-1;mid<=p[i].second;mid++){
					int P=mid-p[i].first+1,Q=p[i].second-mid;
					for(int j=0;j+Q<=n;j++)
						dp[i][j+Q]=max(dp[i][j+Q],dp[i-1][j]+j*P);
				}
			}
		}
		int ans=-1;
		for(int i=0;i<=n;i++)
			ans=max(ans,dp[tot][i]+f(i)+f(n-i));
		cout<<ans<<'\n';
	}
	return 0;
}
posted @ 2024-08-26 11:36  dan-da-dan  阅读(44)  评论(0)    收藏  举报