CF1893C Freedom of Choice
背景
一早上起来打vp,ABC疯狂掉分,我不知道在干嘛,迷迷糊糊40min时才做完C,结果一看D就是个贪心+排序,一下切了,再看E也是比较传统的题(感觉难度B>E>C>D>A?)
题目大意
新定义了一个漂亮集,集有一个值,值的大小等于集的大小在本集合内出现的次数。
输入: 有 m 个可重集,每个集合有 n 个不同的元素,每个元素值为 $a_{i,j}$ 有 $c_{i,j}$ 个。每个集合可以选择 $l_{i}$ 到 $r_{i}$ 个元素组成一个新集,最小化集的值。
输出:组成的新集集的最小值。
思考
设最后组成的新集为 A,那么由于 A 会从每一个多重集中取出$l_{i}$ 到 $r_{i}$ 个元素,当 $∑r_{i}$ - $∑l_{i}$+1 > $∑n_{i}$ 时,可以选出足够多的数,此时答案为0,直接输出。
然后从集合 A 的大小入手,设所有的 l 总和为 suml,所有的 r 总和为 sumr。 $ suml \ \leq\ A_{size}\ \leq\ sumr $ 枚举 A 集合可以删去的数的多少,则 A 可以删去 0 至 sumr-suml+1 个数 对于删去 x 个数,肯定是优先删去值为 $A_{size} - x$ 的数,看最后剩的下几个(注意:这里有可能 x 比值为 $A_{size} - x$ 的数多,所以要判断一下删到 0 就不可以删了)。
下面给出代码,带注释,码风良好。
代码
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
#define int long long
#define inf 1e18
using namespace std;
const int M=1000005;
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x>y?y:x;}
int T,n,m;;
int a[M],c[M],lx[M],rx[M];
int sum[M],p[M],rr[M],pos[M];
//pos是每一个可重集的范围
//将多个可重集压缩到一个数组内,记录每个可重集的左右端点(分块的思想)
int st[M];
inline void work()
{
cin>>m;
int suml=0,sumr=0;//一个是l的总和,一个是r的总和
for(int i=1;i<=m;i++) sum[i]=0;
for(int i=1;i<=m;i++)
{
cin>>n>>lx[i]>>rx[i];
suml+=lx[i],sumr+=rx[i];
for(int j=1+pos[i-1];j<=n+pos[i-1];j++)
{
cin>>a[j];
}
for(int j=pos[i-1]+1;j<=pos[i-1]+n;j++)
{
cin>>c[j];
sum[i]+=c[j];//集合内所有值之和
}
pos[i]=pos[i-1]+n;//n的总和 每个集合的左右端点
}
if(sumr-suml+1>pos[m])
{
cout<<0<<"\n";
return ;//可以选出足够多的数
}
else
{
int cnt=0;
for(int i=1;i<=m;i++)
{
for(int j=pos[i-1]+1;j<=pos[i];j++)
{
if(a[j]>=suml&&a[j]<=sumr&&st[a[j]-suml+1]==0)
{
++cnt;
st[a[j]-suml+1]=1;
}
}
}//可选
for(int i=1;i<=sumr-suml+1;i++) st[i]=p[i]=rr[i]=0;//初始化
if(sumr-suml+1>cnt)
{
cout<<"0"<<"\n";
return ;//可以选出足够多的数
}
for(int i=1;i<=m;i++)
{
for(int j=pos[i-1]+1;j<=pos[i];j++)//枚举每一个集合
{
if(a[j]>=suml&&a[j]<=sumr)
{
int x=sum[i]-c[j];
p[a[j]-suml+1]+=max(0,lx[i]-x);//最多能选的值不为集合大小的个数
rr[a[j]-suml+1]+=rx[i]-min(rx[i],max(x,lx[i]));
//cout<<x<<" "<<a[j]-suml+1<<endl;
}
}
}//注意特判
int minn=inf;
for(int i=1;i<=sumr-suml+1;i++)
{
minn=min(minn,p[i]+max(0,i+suml-sumr-1+rr[i]));//所有情况取最小值
}
cout<<minn<<"\n";
return ;
}
}
signed main()
{
cin>>T;
while(T--)
{
work();
}
return 0;
}