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;
}
posted @ 2023-11-10 10:17  keep_of_silence  阅读(8)  评论(0编辑  收藏  举报  来源
/*
*/