CF1861F 题解
DaiRuiChen007 · · 题解
CF1861F 题解
Problem Link
题目大意
有
n 个人,4 种不同的卡牌,初始第i 个人有a_{i,j} 张第j 种卡牌。你是局外人,手里第
j 种卡牌有b_j 个,你现在要把你的卡牌分给这n 个人,使得分完之后每个人手里的卡牌总数相等,保证有解。第
i 个人最终的分数是手里每种卡牌的数量的最大值。分数最高的那个人如果分数为x ,除了他之外分数最高的人的分数为y ,那么他会获得x-y 的喜悦程度(显然如果存在两个分数最高的,那么他们的喜悦度都是0 ),其他人会获得0 的喜悦程度。对于每个人,算出他最大的可能的喜悦程度。
数据范围:
n\le 5\times 10^4,a_i,b_j\le 10^6 。
思路分析
设
考虑分开处理,枚举第
剩下的就是最小化其余人的最大的得分,显然先二分一个最大值上界
- 源点
S 向B_j 连一条边权为b_j 的边(注意这里的b_j 要去掉第i 个人拿走的卡牌j ),用于控制每种卡牌的总量。 - 然后
B_j 向A_i 连一条边权为k-a_{i,j} 的边(不包括枚举到的这个人),用于控制最大的得分。 - 所有
A_i 向汇点T 连一条边权为up_i 的边,用于控制每个人最终的总卡牌数。
显然答案有解当且仅当所有
考虑最大流转最小割,显然期望最大流
考虑枚举保留那些
然后考虑每个人的贡献,显然若想使
对于每个
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=5e4+5,MAXK=6e6+5;
int a[MAXN][4],b[4],g[16][MAXN],up[MAXN],pc[16],mx[MAXN],val[16][MAXN];
ll sum[16][MAXK],totup;
signed main() {
int n,K=0,le0=0,le1=0;
scanf("%d",&n);
for(int i=1;i<16;++i) pc[i]=1+pc[i&(i-1)];
for(int i=1;i<=n;++i) {
for(int j:{0,1,2,3}) scanf("%d",&a[i][j]),K+=a[i][j];
mx[i]=*max_element(a[i],a[i]+4);
le1=max(le1,min(le0,mx[i])),le0=max(le0,mx[i]);
}
for(int i:{0,1,2,3}) scanf("%d",&b[i]),K+=b[i];
K/=n;
for(int i=1;i<=n;++i) {
for(int j:{0,1,2,3}) up[i]+=a[i][j];
up[i]=K-up[i],totup+=up[i];
for(int s=1;s<16;++s) {
for(int j=0;j<4;++j) if(s&(1<<j)) g[s][i]+=a[i][j];
}
}
for(int s=1;s<16;++s) {
for(int i=1;i<=n;++i) {
int x=(up[i]+g[s][i])/pc[s]+1; //max k: k|s|-g[i][s]<=up[i]
val[s][i]=x,sum[s][0]-=g[s][i],sum[s][x]+=g[s][i]+up[i];
}
sort(val[s]+1,val[s]+n+1);
for(int i=1;i<=K;++i) sum[s][i]+=sum[s][i-1];
}
for(int i=1;i<=n;++i) {
int ans=0,le=(le0==mx[i])?le1:le0;
for(int j:{0,1,2,3}) {
int cur=min(up[i],b[j]),l=le,r=K,res=K;
b[j]-=cur;
function<bool(int)> check=[&](int k) {
for(int s=0;s<16;++s) {
int d=n-(upper_bound(val[s]+1,val[s]+n+1,k)-val[s])+1;
ll cut=sum[s][k]+1ll*d*pc[s]*k;
cut-=min(pc[s]*k-g[s][i],up[i]);
for(int x:{0,1,2,3}) if(!(s&(1<<x))) cut+=b[x];
if(cut<totup-up[i]) return false; //mincut <= expected flow
}
return true;
};
while(l<=r) {
int mid=(l+r)>>1;
if(check(mid)) r=mid-1,res=mid;
else l=mid+1;
}
ans=max(ans,a[i][j]+cur-res),b[j]+=cur;
}
printf("%d ",ans);
}
puts("");
return 0;
}