zoukankan      html  css  js  c++  java
  • 【CF799E】Aquarium decoration 线段树

    【CF799E】Aquarium decoration

    题意:有n个物品,小A和小B各自喜欢其中的某些物品,一件物品可能既被小A喜欢又被小B喜欢,也可能既不被小A喜欢又不被小B喜欢。每个物品都有一个价格$c_i$,让你选出其中的m件物品,满足小A和小B都至少喜欢其中的k件,且总价格最小。

    $nle 2 imes 10^5,c_ile 10^9$

    题解:我们把物品分成四部分:0,A,B,AB,并分别排序,然后我们枚举AB中选了i个。接着我们可以二分:在A,B,0中选择的价格最大的物品的价格mid,假设A中有a个数小于等于mid,则我们要从中选max(a,k)个,同理B中选max(b,k)个,C中选m-i-max(a,k)-max(b,k)个。发现这个显然是可以二分的。如果把二分放到线段树上的话,则时间复杂度是$O(nlog n)$。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define lson x<<1
    #define rson x<<1|1
    using namespace std;
    typedef long long ll;
    const int maxn=200010;
    ll ans;
    int n,m,k,na,nb,nc,nd,N;
    int A[maxn],B[maxn],C[maxn],D[maxn],v[maxn],tag[maxn],p[maxn];
    int ca[maxn<<2],cb[maxn<<2],cd[maxn<<2],ta[maxn],tb[maxn],td[maxn];
    ll sa[maxn],sb[maxn],sd[maxn],ref[maxn];
    
    bool cmp(const int &a,const int &b)
    {
    	return v[a]<v[b];
    }
    void insert(int l,int r,int x,int a,int b,int c)
    {
    	if(c==0)	ca[x]=b;
    	if(c==1)	cb[x]=b;
    	if(c==2)	cd[x]=b;
    	if(l==r)	return ;
    	int mid=(l+r)>>1;
    	if(a<=mid)	insert(l,mid,lson,a,b,c);
    	else	insert(mid+1,r,rson,a,b,c);
    }
    ll query(int l,int r,int x,int a)
    {
    	if(l==r)
    		return sa[max(ca[x],a)]+sb[max(cb[x],a)]+sd[cd[x]]-ref[l]*(max(ca[x],a)+max(cb[x],a)+cd[x]+k-a-m);
    	int mid=(l+r)>>1,t=max(ca[lson],a)+max(cb[lson],a)+cd[lson]+k-a;
    	if(t>=m)	return query(l,mid,lson,a);
    	return query(mid+1,r,rson,a);
    }
    void build(int l,int r,int x)
    {
    	if(l==r)
    	{
    		ca[x]=ta[l],cb[x]=tb[l],cd[x]=td[l];
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(l,mid,lson),build(mid+1,r,rson);
    	ca[x]=ca[rson],cb[x]=cb[rson],cd[x]=cd[rson];
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd(),k=rd();
    	int i,a,b;
    	for(i=1;i<=n;i++)	v[i]=rd(),p[i]=i;
    	sort(p+1,p+n+1,cmp);
    	for(i=1;i<=n;i++)
    	{
    		if(i==1||v[p[i]]>ref[N])	ref[++N]=v[p[i]];
    		v[p[i]]=N;
    	}
    	a=rd();
    	for(i=1;i<=a;i++)	b=rd(),tag[b]|=1;
    	a=rd();
    	for(i=1;i<=a;i++)	b=rd(),tag[b]|=2;
    	for(i=1;i<=n;i++)
    	{
    		if(tag[i]==0)	D[++nd]=ref[v[i]],td[v[i]]++;
    		if(tag[i]==1)	A[++na]=ref[v[i]],ta[v[i]]++;
    		if(tag[i]==2)	B[++nb]=ref[v[i]],tb[v[i]]++;
    		if(tag[i]==3)	C[++nc]=ref[v[i]];
    	}
    	sort(A+1,A+na+1),sort(B+1,B+nb+1),sort(C+1,C+nc+1),sort(D+1,D+nd+1);
    	for(i=1;i<=N;i++)	ta[i]+=ta[i-1],tb[i]+=tb[i-1],td[i]+=td[i-1];
    	for(i=1;i<=na;i++)	sa[i]=sa[i-1]+A[i];
    	for(i=1;i<=nb;i++)	sb[i]=sb[i-1]+B[i];
    	for(i=1;i<=nd;i++)	sd[i]=sd[i-1]+D[i];
    	ll sum=0;	ans=1ll<<60;
    	build(0,N,1);
    	for(i=0;i<=min(nc,m);i++)
    	{
    		sum+=C[i];
    		if(min(na,nb)>=k-i&&2*k-i<=m&&i+na+nb+nd>=m)
    			ans=min(ans,sum+query(0,N,1,k-i));
    	}
    	if(ans==1ll<<60)	puts("-1");
    	else	printf("%lld",ans);
    	return 0;
    }
  • 相关阅读:
    Shell 脚本读取文件中的每行
    Linux中的内存管理机制
    CPU Cache 学习(一)
    linux系统层次(转)
    linux下的一些常用命令
    几个关于Linux进程fork()的题目
    GDB调试工具
    POSIX thread library 简介I
    Scheme Programming language II
    Google Chrome浏览器标签页之间的自动切换
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/8503753.html
Copyright © 2011-2022 走看看