• [CSP-S模拟测试]:赛(贪心+三分)


    题目描述

    由于出题人思维枯竭所以想不出好玩的背景。
    有$n$个物品,第$i$个物品的价格是$v_i$,有两个人,每个人都喜欢$n$个物品中的一些物品。
    要求选出正好$m$个物品,满足选出的物品中至少有$k$个物品被第一个人喜欢,$k$个物品被第二个人喜欢。并求出最小的价格和。


    输入格式

    第一行三个数$n,m,k$。
    第二行$n$个数,第$i$个数表示$v_i$。
    第三行包含一个数$a$,表示第一个人喜欢的物品数。
    第四行包含$a$个数,表示第一个人喜欢的物品是哪几个。
    第五行包含一个数$b$,表示第二个人喜欢的物品数。
    第六行包含$b$个数,表示第二个人喜欢的物品是哪几个。


    输出格式

    一个数表示答案。若不存在合法的方案则输出$-1$。


    样例

    样例输入:

    4 3 2
    3 2 2 1
    2
    1 2
    2
    1 3

    样例输出:

    7


    数据范围与提示

    对于测试点$1sim 4$:$nleqslant 20$。
    对于测试点$5sim 10$:不存在一个物品被两个人喜欢。
    对于测试点$11sim 15$:$nleqslant 2 imes 10^3$。
    对于测试点$16sim 20$:无特殊限制。
    对于所有的数据,$nleqslant 2 imes 10^5,m,kleqslant n,v_ileqslant 10^9$。


    题解

    这道题优秀的随机化可以拿到$95$分……

    我们可以设两个人喜欢的物品交集个数为$r$,那么我们就可以贪心了。

    发现答案满足单谷,于是我们可以三分。

    其实三分也是存在漏洞的,因为一段的$r$可能对应一样的答案,但是显然随机数据没有卡。

    时间复杂度:$Theta(nlog k)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int N,M,K,A,B;
    int v[200001];
    bool a[200001],b[200001];
    int que[4][200001],top1,top2,top3,top4;
    long long ans=1LL<<60;
    bool cmp(int x,int y){return v[x]<v[y];}
    long long judge(int x)
    {
    	long long res=0;
    	for(int i=1;i<=x;i++)res+=v[que[3][i]];
    	for(int i=1;i<=K-x;i++){res+=v[que[1][i]];res+=v[que[2][i]];}
    	int lst=M-x-2*max(K-x,0);
    	int flag1=max(K-x,0)+1;
    	int flag2=max(K-x,0)+1;
    	int flag3=1;
    	while(lst)
    	{
    		if(v[que[1][flag1]]<=v[que[2][flag2]]&&v[que[1][flag1]]<=v[que[0][flag3]])
    		{
    			res+=v[que[1][flag1]];
    			flag1++;
    		}
    		else if(v[que[2][flag2]]<=v[que[1][flag1]]&&v[que[2][flag2]]<=v[que[0][flag3]])
    		{
    			res+=v[que[2][flag2]];
    			flag2++;
    		}
    		else
    		{
    			res+=v[que[0][flag3]];
    			flag3++;
    		}
    		lst--;
    	}
    	return res;
    }
    int main()
    {
    	scanf("%d%d%d",&N,&M,&K);
    	for(int i=1;i<=N;i++)scanf("%d",&v[i]);
    	scanf("%d",&A);
    	for(int i=1;i<=A;i++)
    	{
    		int x;
    		scanf("%d",&x);
    		a[x]=1;
    	}
    	scanf("%d",&B);
    	for(int i=1;i<=B;i++)
    	{
    		int x;
    		scanf("%d",&x);
    		b[x]=1;
    	}
    	for(int i=1;i<=N;i++)
    	{
    		if(!a[i]&&!b[i])que[0][++top1]=i;
    		if( a[i]&&!b[i])que[1][++top2]=i;
    		if(!a[i]&& b[i])que[2][++top3]=i;
    		if( a[i]&& b[i])que[3][++top4]=i;
    	}
    	if(top2+top4<K||top3+top4<K||top4<max(2*K-M,0)||M<K||min(top4,K)+2*max(K-top4,0)>M){puts("-1");return 0;}
    	sort(que[0]+1,que[0]+top1+1,cmp);
    	sort(que[1]+1,que[1]+top2+1,cmp);
    	sort(que[2]+1,que[2]+top3+1,cmp);
    	sort(que[3]+1,que[3]+top4+1,cmp);
    	v[0]=0x3f3f3f3f;
    	int lft=max(K-min(top2,top3),max(2*K-M,0));
    	int rht=min(K,top4);
    	while(rht-lft>2)
    	{
    		int midl=lft+(rht-lft)/3;
    		int midr=rht-(rht-lft)/3;
    		if(judge(midl)<judge(midr))rht=midr;
    		else lft=midl;
    	}
    	ans=min(ans,judge(lft));
    	ans=min(ans,judge(lft+1));
    	ans=min(ans,judge(rht));
    	if(ans==(1LL<<60))puts("-1");
    	else printf("%lld",ans);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    [计算机网络] HTTPDNS 协议
    [计算机网络] DNS 协议
    [计算机网络] P2P 协议
    [年中总结]一个骄傲而又自卑的人的内心独白
    [计算机网络] FTP 协议
    [计算机网络]简单聊聊套接字 Socket
    扒一扒自从买了kindle后看的书
    安全学习笔记——缓冲区溢出攻击
    思想感悟
    C#利用服务器实现客户端之间通信
  • 原文地址:https://www.cnblogs.com/wzc521/p/11615892.html
Copyright © 2020-2023  润新知