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\) ,则以下条件成立:
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;
}

浙公网安备 33010602011771号