Wannafly挑战赛20 A 染色 B 背包

A 染色
当时看了一下题目,觉得还挺简单的,想了一下思路,大概15分钟A了
思路大概是这样的:反正要变成的颜色肯定是树上某个点的颜色,我们枚举树上所有的点变成某个点颜色的代价,取其中的最小值即可,可以线性得到,因为树上所有的点变成某个点node颜色的代价为 =树上所有点的价值-不需要改变颜色点的价值和+需要改变颜色点的数量*点node的价值
这些价值相同的数量,我是用map存的

#include<bits/stdc++.h>
using namespace std;
 
typedef long long ll;
const int maxn=100000+100;
 
ll ans[maxn];
 
int main(){
     
    map<ll,int>M;
    map<ll,int>cnt;
    int n;
    scanf("%d",&n);
    ll sum=0;
    for(int i=1;i<=n;i++) scanf("%lld",&ans[i]),sum+=ans[i],cnt[ans[i]]++;
    int u,v;
    for(int i=1;i<n;i++) scanf("%d%d",&u,&v);
    ll Min=1e18;
    for(int i=1;i<=n;i++){
         
        if(M[ans[i]]) continue; //当然可以不要,不影响结果,习惯加上
        M[ans[i]]=1;
        ll tmp=sum-(ll)cnt[ans[i]]*ans[i]+(ll)(n-cnt[ans[i]])*ans[i];
        Min=min(Min,tmp);
    }
    printf("%lld\n",Min);
}

B 背包
晚上比赛的时候也是用的相同的方法,但是没有考虑到m是偶数的特殊情况,导致只过了90%的数据
思路大概是这样的:
结构体存储物品
结构体按物品的价值从小到大排序

1.当m是奇数时,这个就好办了,我们把要取的数分成三段取,即前中后, 个数分别为(m-1)/2 ,1 , (m-1)/2,比如取7个数, 前取 3 ,中取1 ,后取 3
所有我们可以枚举中间这个数,但是首先要把中间这个数的前面和后面的(m-1)/2个最小容量值预处理出来,可以用优先队列进行处理,具体看代码。


2.当m是偶数时,要麻烦一点,我们还是把要取的数分成三段取,即前中后, 个数分别为(m-1)/2 ,1 , (m-1)/2+1,比如取8个数, 前取 3 ,中取1 ,后取 4
我们依然枚举中间这个数,因为此时平均值=(中间这个数+后面取的第一个数)/2,所以后面取(m-1)/2+1个数的时候,第一个数越往后面取平均值就越大(当然要符合情况的条件下),这里可以二分得到,仔细想一想为什么可以二分。
取后面的数为什么可以二分:假设在区间 [l,n]中取 (m-1)/2+1个数符合情况,那在区间 [l-1,n]中取(m-1)/2+1个数肯定符合情况,因为还多了一个数,所以当 [l,n] 满足情况,我们就不需要管 l 前面的数了,因为不会有更大的平均值,所以我们考虑 l 后面的数即可,这就符合二分的性质了

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=100000+100;

priority_queue<ll,vector<ll>,less<ll> >lque,rque;
ll l[maxn],r[maxn];
struct Node{
	
	ll v,c;
}node[maxn];
ll v;
int n,m;
int l_num,r_num;

inline bool cmp(Node a,Node b){
	
	return a.v<b.v;
}

void slove(){
	
	l_num=r_num=(m-1)/2;
	ll sum=0;
	for(int i=1;i<=l_num;i++) sum+=node[i].c,lque.push(node[i].c);
	l[l_num]=sum;
	for(int i=l_num+1;i<=n;i++){
		
		if(lque.empty()) continue;
		ll tmp=lque.top();
		if(tmp>node[i].c){
			
			sum-=tmp;
			sum+=node[i].c;
			lque.pop();
			lque.push(node[i].c);
		}	
		l[i]=sum;
	}
	
	if(m%2==0) r_num++;
	sum=0;
	for(int i=n;i>n-r_num;i--) sum+=node[i].c,rque.push(node[i].c);
	r[n-r_num+1]=sum;
	for(int i=n-r_num;i>=1;i--){
		
		ll tmp=rque.top();
		if(tmp>node[i].c){
			
			sum-=tmp;
			sum+=node[i].c;
			rque.pop();
			rque.push(node[i].c);
		}
		r[i]=sum;
	}
}

int main(){
	
	scanf("%lld%d%d",&v,&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld%lld",&node[i].v,&node[i].c);
	sort(node+1,node+n+1,cmp);
	
	slove();
	if(m%2){
		
		ll Max=0;
		for(int i=n-r_num;i>l_num;i--){
			
			if(r[i+1]+l[i-1]+node[i].c<=v){
				
				Max=node[i].v;
				break;
			}
		}
		printf("%lld\n",Max);
	}
	else{
		
		ll Max=0;
		for(int i=n-r_num;i>l_num;i--){
			
			int L=i+1;
			int R=n-r_num+1;
            int key=-1;
			while(L<=R){
				
				int mid=(L+R)>>1;
				if(r[mid]+l[i-1]+node[i].c<=v) L=mid+1,key=mid;
				else R=mid-1;
			}
			if(key==-1) continue;
			Max=max(Max,(node[i].v+node[key].v)/2);
		}
		printf("%lld\n",Max);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ITKaven

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值