苦力
首先考虑 \(m=1\),对于两个人 \(i<j\),考虑两人对答案的贡献,显然有 \(0\) 或 \(\frac{1}{2}\) 或 \(1\) 三种贡献。
我们断言,\(m=1\) 时一定是一段前缀从左边进入,后缀从右边进入。证明考虑若不按照这么排列,那么交换两个人一定不劣。
因此 \(m=1\) 的答案为 \(\min(\frac{i(i-1)+(n-i)(n-i-1)}{4})\),这里用到了经典结论长度为 \(n\) 的排列的逆序对期望数是 \(\frac{n(n-1)}{4}\)。
观察到 \(m\) 并不大,考虑状压,\(f_{S}\) 代表已经加入了 \(S\) 这个集合的最小代价。枚举最后一个加入的候考室 \(i\),如果枚举上面所提到的分界点,那么时间复杂度为 \(O(2^mn)\)。
考虑进一步拆贡献,如果能确定下来分界点,此时我们还需要 \(S-i\) 的前缀和信息,将前缀和信息拆到每一个 \(j\in S\) 上。令 \(g_{i,j,k}\) 代表先加入 \(j\) 再加入 \(i\),同时选择分界点为 \(k\),此时 \(j\) 对 \(i\) 产生的贡献,状态数是 \(O(nm)\) 的。
\(g\) 看上去并不好求。考虑增量维护 \(g\),观察到 \(g\) 的二阶差分很好维护,因此维护 \(g\) 的二阶差分,然后做两遍前缀和即可。时间复杂度 \(O(nm)\)。
如果对 \(f\) 进行转移时只有 \(g\) 这一个量,那么很好转移(暂且不表)。但是还存在 \(i\) 这个候考室内部产生贡献,如果我们钦定了分界点是 \(j\),那么此时的贡献为:
\(s_i\) 为 \(i\) 候考室的人数。
认真观察,不难发现前面的式子下凸,后面的式子也下凸。前面的证明考虑最低点一定是中点,后面类似。那么两个下凸的函数相加,得到的也是一个下凸的函数,在这个下凸函数上二分决策点即可。时间复杂度 \(O(nm^2+2^mm^2\log n)\)。
注意二分决策点时不要取模,状压 dp 时也不要取模,可以只记录分子进行转移。
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pii std::pair<int,int>
#define vint std::vector<int>
#define vpair std::vector<pii>
#define debug(...) fprintf(stderr,##__VA_ARGS__)
template<typename T>
void read(T &x){
x=0;
int f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
x*=f;
}
std::stack<char>st;
template<typename T>
void print(T x){
if(x==0) putchar('0');
if(x<0) putchar('-'),x=-x;
while(st.size()) st.pop();
while(x) st.push((char)('0'+x%10)),x/=10;
while(st.size()) putchar(st.top()),st.pop();
}
template<typename T>
void printsp(T x){
print(x),putchar(' ');
}
template<typename T>
void println(T x){
print(x),putchar('\n');
}
template<typename T,typename I>
bool chkmin(T &a,I b){
if(a>b) return a=b,1;
return 0;
}
template<typename T,typename I>
bool chkmax(T &a,I b){
if(a<b) return a=b,1;
return 0;
}
template<typename T,typename I>
void addedge(std::vector<I>*vec,T u,T v){
vec[u].push_back(v);
}
template<typename T,typename I,typename K>
void addedge(std::vector<K>*vec,T u,T v,I w){
vec[u].push_back({v,w});
}
template<typename T,typename I>
void addd(std::vector<I>*vec,T u,T v){
addedge(vec,u,v),addedge(vec,v,u);
}
template<typename T,typename I,typename K>
void addd(std::vector<K>*vec,T u,T v,I w){
addedge(vec,u,v,w),addedge(vec,v,u,w);
}
bool Mbe;
const int inf=1e18,MOD1=998244353,MOD2=1e9+7;
const int maxn=2e5+10,maxm=17,lim=(1ll<<16)+10;
int a[maxn],s[maxm],f[maxm][maxn],h[maxm][maxn],g[lim],n,m,qzh[maxm][maxn];
vint vec[maxn];
int ksm(int x,int y,int p){
if(y==0) return 1;
int z=ksm(x,y>>1,p);
z=z*z%p;
if(y&1) z=z*x%p;
return z;
}
void add(int &x,int y){
x+=y;
if(x>=MOD2) x-=MOD2;
}
int calc(int x){
return x*(x-1)%MOD2*ksm(4,MOD2-2,MOD2)%MOD2;
}
double dcalc(int x){
double res=1.0*x;
return res*(res-1.0)/4.0;
}
double get(int i,int j,int mid){
double cost=0;
for(int k=1;k<=m;k++){
if((i&(1ll<<(k-1)))==0) continue;
if(k==j) continue;
cost+=f[k][vec[j][mid]]+h[k][vec[j][mid+1]];
}
double x=cost*1.0;
cost+=dcalc(mid)+dcalc(s[j]-mid);
return cost;
}
int gett(int i,int j,int mid){
int cost=0;
for(int k=1;k<=m;k++){
if((i&(1ll<<(k-1)))==0) continue;
if(k==j) continue;
cost+=f[k][vec[j][mid]]+h[k][vec[j][mid+1]];
}
cost*=4;
cost+=mid*(mid-1)+(s[j]-mid)*(s[j]-mid-1);
return cost;
}
int baoli(int i,int j){
double mi=1e18;
int id=-1;
for(int mid=0;mid<=s[j];mid++)
if(chkmin(mi,get(i,j,mid))) id=mid;
return id;
}
bool Men;
signed main(){
freopen("data.in","r",stdin);
freopen("A.out","w",stdout);
debug("%.6lfMB\n",(&Mbe-&Men)/1048576.0);
read(n),read(m);
bool flag=0;
if(n<=(int)5e4&&m<=16) flag=1;
for(int i=1;i<=m;i++) vec[i].push_back(0);
for(int i=1;i<=n;i++) read(a[i]),s[a[i]]++,vec[a[i]].push_back(i);
for(int i=1;i<=m;i++){
int z=1;
for(int j=1;j<=n;j++){
qzh[i][j]=qzh[i][j-1];
if(z<vec[i].size()&&vec[i][z]==j) z++,qzh[i][j]++;
}
}
for(int i=1;i<=m;i++) vec[i].push_back(n+1);
for(int i=1;i<=m;i++){
for(int k=1;k<=s[i];k++){
for(int p=vec[i][k-1]+1;p<=vec[i][k];p++) f[a[p]][vec[i][k]]++;
}
for(int k=s[i];k>=0;k--){
for(int p=vec[i][k+1]-1;p>=vec[i][k];p--) h[a[p]][vec[i][k]]++;
}
for(int k=1;k<=s[i];k++)
for(int j=1;j<=m;j++) f[j][vec[i][k]]+=f[j][vec[i][k-1]];
for(int k=1;k<=s[i];k++)
for(int j=1;j<=m;j++) f[j][vec[i][k]]+=f[j][vec[i][k-1]];
for(int k=s[i];k>=0;k--) for(int j=1;j<=m;j++) h[j][vec[i][k]]+=h[j][vec[i][k+1]];
for(int k=s[i];k>=0;k--) for(int j=1;j<=m;j++) h[j][vec[i][k]]+=h[j][vec[i][k+1]];
}
int lim=(1ll<<m);
for(int i=0;i<lim;i++) g[i]=inf;
g[0]=0;
for(int i=1;i<lim;i++){
for(int j=1;j<=m;j++){
if((i&(1ll<<(j-1)))==0) continue;
int l=0,r=s[j];
int mid;
if(flag) mid=baoli(i,j);
else {
while(l<=r){
int mid=(l+r)>>1;
double now=get(i,j,mid);
if(mid==s[j]){
r=mid-1;
continue;
}
double nxt=get(i,j,mid+1);
if(now<=nxt) r=mid-1;
else l=mid+1;
}
mid=r+1;
}
int cost=gett(i,j,mid);
int state=i-(1ll<<(j-1));
chkmin(g[i],cost+g[state]);
}
}
int ans=g[lim-1]%MOD2;
ans=ans*ksm(4,MOD2-2,MOD2)%MOD2;
println(ans);
debug("%.6lfms\n",1e3*clock()/CLOCKS_PER_SEC);
}