题解:P11085 [ROI 2019] 学生座位 (Day 2)
chen_yu_hang · · 题解
模拟赛考到这题,推出前面贪心和决策单调了,忘了怎么分治,
首先容易想到可以把每个班的同学排序,相邻两两一组,这样一定最优。证明可以尝试将两组的同学互换一下,一定更劣。
然后发现如果存在两个桌子满足
然后编组后每班的每组就是一个独立的问题,如果直接枚举桌子,暴力做是
仔细观察发现由于每个班身高递增,那么桌子也一定要递增,这是显然的。证明还是可以尝试互换桌子,一定更劣。然后发现其实就是决策单调性,可以分治。
具体来说,solve(ll l1,ll r1,ll l2,ll r2) 处理区间 solve(l1,mid-1,l2,pos) 和 solve(mid+1,r1,pos,r2) 即可。
因为每个组之间答案互相独立,所以左右边的答案不会影响中间,所以可以分治。
一共递归
:::success[
//#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+10,INF=0x3f3f3f3f3f3f3f3f;
vector<vector<ll>> h;
ll w[N<<1],s[N<<1];
ll l[N],r[N],L[N],R[N];
ll m,n,k,ans;
bool cmp(ll x,ll y)
{
return L[x]==L[y]?R[x]>R[y]:L[x]<L[y];
}
pair<ll,ll> calc(ll i,ll a,ll b)
{
for(int j=1;j<=m;j++)
w[2*j-1]=h[j][2*i-1],w[2*j]=h[j][2*i];
sort(w+1,w+1+2*m); s[1]=w[1];
for(int j=2;j<=2*m;j++) s[j]=s[j-1]+w[j];
ll mi=INF,id=0,lw=0,rw=0;
for(int j=a;j<=b;j++)
{
while(lw<2*m&&w[lw+1]<=l[j]) lw++;
while(rw<2*m&&w[rw+1]<=r[j]) rw++;
ll nv=lw*l[j]-s[lw]+s[2*m]-s[rw]-(2*m-rw)*r[j];
if(nv<mi) mi=nv,id=j;
}
return {mi,id};
}
void solve(ll l1,ll r1,ll l2,ll r2)
{
if(l1>r1) return;
ll mid=(l1+r1)>>1;
auto [val,pos]=calc(mid,l2,r2);
ans+=val;
solve(l1,mid-1,l2,pos);
solve(mid+1,r1,pos,r2);
}
int main()
{
// freopen("class.in","r",stdin);
// freopen("class.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>m>>n>>k;
for(int i=1;i<=k;i++) cin>>L[i]>>R[i];
h.resize(m+1);
for(int i=1;i<=m;i++)
{
h[i].resize(2*n+1);
for(int j=1;j<=2*n;j++) cin>>h[i][j];
sort(h[i].begin()+1,h[i].begin()+1+2*n);
}
for(int i=1;i<=k;i++) w[i]=i;
sort(w+1,w+1+k,cmp); ll la=1,kk=0;
for(int i=2;i<=k;i++)
if(L[w[la]]<=L[w[i]]&&R[w[i]]<=R[w[la]]) w[i]=0;
else la=i;
for(int i=1;i<=k;i++)
if(w[i]) l[++kk]=L[w[i]],r[kk]=R[w[i]];
k=kk,solve(1,n,1,k),cout<<ans;
return 0;
}
:::