感觉挺一眼的啊?
众所周知如果序列 \(i\) 要通过相邻两项交换变成 \(p_i\),那么交换次数就是 \(\sum_{i<j}[p_i>p_j]\),或者说线段 \((i,p_i)\) 相交的对数。
于是一个很 naive 的想法就是枚举最终序列的黑白状态,但是这样显然行不通。
一看数据范围,\(n=2000\),是不是能在上面做点手脚。
容易发现黑色球的序列和白色球的序列是单调的,所以只需要知道某个球前面有几个黑球几个白球以及这个球是什么颜色就能知道这个球的位置和权值应该是多少。
于是考虑 DP,设 \(dp[n][m]\) 表示前 \(n\) 个黑球和前 \(m\) 个白球的线段最多有几个相交,容易发现就是 \(i\) 比自己小 \(p_i\) 比自己大的。
这个数线段非常容易,因为 \(n\) 只有 \(2000\) 所以可以直接暴力做二维偏序。
然后基本上就做完了,复杂度 \(O(n^2)\),需要注意一些细节。
#include<cstdio>
#include<cctype>
const int M=2005;
int n,S1[M][M*2],S2[M][M*2],dp[M][M],p1[M],p2[M];
inline char read_c(){
char s;while(!isalpha(s=getchar()));return s;
}
inline int read(){
int n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s&15),isdigit(s=getchar()));return n;
}
inline int min(const int&a,const int&b){
return a>b?b:a;
}
signed main(){
n=read();
for(int i=1;i<=n*2;++i){
bool typ=read_c()=='W';(typ?p1:p2)[read()]=i;
}
for(int i=1;i<=n;++i)++S1[i][p1[i]],++S2[i][p2[i]];
for(int i=1;i<=n;++i)for(int j=1;j<=n*2;++j)S1[i][j]+=S1[i-1][j],S2[i][j]+=S2[i-1][j];
for(int i=1;i<=n;++i)for(int j=1;j<=n*2;++j)S1[i][j]+=S1[i][j-1],S2[i][j]+=S2[i][j-1];
for(int i=0;i<=n;++i)for(int j=0;j<=n;++j){
const int&w1=(j?dp[i][j-1]+(i-S1[i][p2[j]-1])+(j?j-1-S2[j-1][p2[j]-1]:0):1e9);
const int&w2=(i?dp[i-1][j]+(i?i-1-S1[i-1][p1[i]-1]:0)+(j-S2[j][p1[i]-1]):1e9);
dp[i][j]=!i&&!j?0:min(w1,w2);
}
printf("%d",dp[n][n]);
}
标签:const,int,题解,read,while,ARC097E,return,getchar
From: https://www.cnblogs.com/lmpp/p/16599709.html