题解:P8394 [BalticOI 2022] Boarding Passes (Day2)
Xueqiaorui47 · · 题解
题意:求
由
由期望的线性性,有
设
情况 A:
u 和v 不在同一组:
- 当
u 比v 早登船,则v 是否经过u 与v 的方向唯一相关,没有随机性。- 当
v 比u 早登船时同理。结论:对于不同组的乘客,是否产生贡献只与所属组的登船顺序及后上船组的登船方向有关,没有随机性。
情况 B:
u 和v 在同一组:
- 当两人都从左边上时:若
u 比v 早上,此时贡献为1 ,否则贡献为0 ,总贡献E=0.5\times1+0.5\times 0=0.5 ;- 当两人都从右边上时,同理可得为
0.5 ;- 当
u 从左边,v 从右边上时,可发现无论如何不会相遇,贡献为0 ;- 当
u 从右边,v 从左边上时,可发现必定相遇,贡献为1 ;结论:对于同一组的乘客,对期望的贡献与上船的方向组合有关,可能为
0.5 ,0.5 ,1 ,0 。
接下来考虑序列中的逆序对(
把贡献分类考虑:
Part1:组内贡献
Part2:组间贡献
这个东西可以双指针预处理,代码:
因为贡献存在 inner 变为 outer 累加时
vector<vector<vector<int>>>C(g,vector<vector<int>>(g));
vector<int>L(n+2,0);
vector<int>R(n+2,0);
for(int x=0;x<g;++x)
for(int y=0;y<g;++y)
{
if(x==y)continue;
int m=gra[y].size();
C[x][y].assign(m+1,0);
for(int k=0;k<=m;++k)L[k]=R[k]=0;
int k=0;
int size=gra[x].size();
for(int i=0;i<m;++i)
{
while(k<size&&gra[x][k]<gra[y][i])++k;
L[i]=k,R[i]=size-k;
}
for(int i=1;i<=m;++i)L[i]+=L[i-1];
for(int i=m-1;i>=0;--i)R[i]+=R[i+1];
for(int i=0;i<=m;++i)C[x][y][i]=(i!=0?L[i-1]:0)+(i!=m?R[i]:0);
}
预处理完就可以做 DP 了,设 dp[mask] 为已上船的组的掩码为 mask 时的最小代价,则有
朴素实现是
注意到因为
:::info[证明凸性]
下证总贡献
Part1:组内贡献为
Part2:
接下来只需证
即可。又因为组内各元素位置单调不降,所以
| 综上, |
|---|
:::success[完整代码]
#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int INF=0x3f3f3f3f3f3f3f3f;
constexpr int maxg=15;
int toint[256];
int dp[1<<maxg];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
memset(toint,-1,sizeof(toint));
memset(dp,0x3f,sizeof(dp));
string s;
cin>>s;
int n=s.size();
s=' '+s;
int g=0;
vector<vector<int>>gra;
for(int i=1;i<=n;++i)
{
if(toint[s[i]]==-1)
{
toint[s[i]]=g++;
gra.emplace_back();
}
gra[toint[s[i]]].emplace_back(i);
}
vector<vector<vector<int>>>C(g,vector<vector<int>>(g));
vector<int>L(n+2,0);
vector<int>R(n+2,0);
for(int x=0;x<g;++x)
for(int y=0;y<g;++y)
{
if(x==y)continue;
int m=gra[y].size();
C[x][y].assign(m+1,0);
for(int k=0;k<=m;++k)L[k]=R[k]=0;
int k=0;
int size=gra[x].size();
for(int i=0;i<m;++i)
{
while(k<size&&gra[x][k]<gra[y][i])++k;
L[i]=k,R[i]=size-k;
}
for(int i=1;i<=m;++i)L[i]+=L[i-1];
for(int i=m-1;i>=0;--i)R[i]+=R[i+1];
for(int i=0;i<=m;++i)C[x][y][i]=(i!=0?L[i-1]:0)+(i!=m?R[i]:0);
}
auto calc=[&](unsigned mask,int y,int k)
{
int ans=0;
int m=gra[y].size();
int inner=k*(k-1)/2+(m-k)*(m-k-1)/2;
int outer=0;
for(int x=0;x<g;++x)
if(mask>>x&1)outer+=C[x][y][k]*2;
return inner+outer;
};
auto find=[&](unsigned mask,int y)
{
int l=0,r=gra[y].size();
while(r-l>=10)
{
int lmid=(l*2+r)/3;
int rmid=(l+r*2)/3;
if(calc(mask,y,lmid)>calc(mask,y,rmid))l=lmid;
else r=rmid;
}
return make_pair(l,r);
};
dp[0]=0;
for(unsigned mask=0;mask<1<<g;++mask)
{
for(int y=0;y<g;++y)
{
if(mask>>y&1)continue;
auto [l,r]=find(mask,y);
for(int k=l;k<=r;++k)
{
dp[mask|1<<y]=min(dp[mask|1<<y],dp[mask]+calc(mask,y,k));
}
}
}
unsigned full=(1<<g)-1;
cout<<fixed<<setprecision(10)<<1.0L*dp[full]/2;
}
:::