NOI2017 day2T2 vegetables 题解(伪)

本文介绍了一种关于蔬菜买卖的优化算法。该算法通过优先队列处理不同价值与变质时间的蔬菜,确保高价值蔬菜优先销售,并利用并查集等数据结构优化计算复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(同步赛的时候day2的T2T3都爆0了...我这么弱还是回家搞文化吧)

这题首先注意题意.我们发现题意实际上是说,有很多份能买一次的蔬菜,它们有一个价值和一个变质时间.第一次卖的每种蔬菜可以认为是最后变质的(我们肯定希望更有价值的更好卖)

接下来把所有蔬菜按价值从大到小放在一个优先队列里(或者说直接排序也可以),对于每份蔬菜,如果能刚好在它变质那一天卖掉,就在它变质那一天卖掉;否则看变质前一天...以此类推.正确性的话...放弃一份价值高的蔬菜最多能多卖出一份价值低的蔬菜,肯定是不划算的.更晚卖肯定比更早卖优.

用上面的方法加上一些优化大概有80分.

考虑先算出p最大的那一天的收益.把每种蔬菜放进优先队列里(第一次卖的单独再算一种),接下来从这种蔬菜最后变质的一份变质的那一天开始,如果那一天能卖完就卖完,否则就会有剩余到前一天,以此一直推到第0天.注意两个保证复杂度的细节:用并查集快速处理连续的不能再卖东西的几天;如果一种蔬菜不会变质,而且已经没有剩余,则要及早跳出循环.

上面这个过程的复杂度大约是(2nlogn+m*max(p)*(并查集复杂度)),大约是1e6或者1e7级别的.

接下来计算之前每一天的收益.把所有可以被卖掉的蔬菜放进一个优先队列里.每当减少一天时,就把价值最小的m份蔬菜的价值减掉.这样就处理出了每一天的收益.复杂度(m*max(p)*log(m*max(p))),大约是1e7级别.(这个过程可以看做是把最后一天的蔬菜替换掉了之前价值最小的蔬菜的蔬菜)

至此,这个问题就被解决了.这只是我的方法,如果有错误欢迎指出QAQ.

(赛场上看错题了真是***)

具体细节见代码.(顺便,%%%dy)

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<vector>
#include<queue>
using namespace std;
const int maxm=100000;//maxm是最大天数
int Min(int x,int y){
	return x<y?x:y;
}
int Max(int x,int y){
	return x>y?x:y;
}
struct Node{
	int a,s,c,x;
	void readl(){
		scanf("%d%d%d%d",&a,&s,&c,&x);
	}
}b[100100];
struct node{
	int a,c,x;
}d[200100];
bool cmp(const node &A,const node &B){
	return A.a>B.a;
}
int g[1001000];
int num,cnt;
int m;
int f[100100];
int dis[100100];
long long dp[100100];
int getfa(int x){//用一种鬼畜方式写的并查集
	if(dis[f[x]]<m) return f[x];
	return f[x]=getfa(f[x]-1);
}
int main()
{
	freopen("vegetables.in","r",stdin);
	freopen("vegetables.out","w",stdout);
	int n,T,cas;
	scanf("%d%d%d",&n,&m,&T);
	for(int i=1;i<=n;i++)
		b[i].readl();
	for(int i=1;i<=n;i++)
	{
		if(b[i].c>1)
			d[++num]=(node){b[i].a,b[i].c-1,b[i].x};
		if(b[i].x==0)
			d[++num]=(node){b[i].a+b[i].s,maxm,-1};//把第一次卖的单独考虑,注意特殊处理x=0的情况
		else
			d[++num]=(node){b[i].a+b[i].s,(b[i].c-1)/b[i].x+1,-1};
	}
	sort(d+1,d+num+1,cmp);
	for(int i=1;i<=100000;i++)
		f[i]=i;
	node st;
	int ps,s,t,lef;
	long long ans=0;
	for(int i=1;i<=num;i++)
	{
		st=d[i];
		if(st.x==-1)
		{
			t=getfa(Min(st.c,maxm));
			if(t<=0) continue;
			dis[t]++,g[++cnt]=st.a;
			ans=ans+st.a;
		}
		else
		{
			t=(st.x==0?maxm:Min(maxm,(st.c-1)/st.x+1));	//最后变质的一天,同样注意x==0的情况
			lef=st.c-st.x*(t-1);//当前剩余可以卖的蔬菜数量
			while(t>0)
			{
				while(lef>0&&dis[t]<m)
				{
					lef--;
					dis[t]++,g[++cnt]=st.a;
					ans=ans+st.a;
				}
				if(lef==0&&st.x==0) break;//如果不加这句话,就会导致进入大循环次数过多
				s=getfa(t-1);
				lef+=(t-s)*st.x;
				t=s;
			}
		}
	}
	dp[maxm]=ans;
	cnt=maxm*m;
	sort(g+1,g+cnt+1);//g是所有卖出去了的蔬菜
	int ilem=1;
	for(int i=maxm-1;i>=1;i--)
	{
		for(int j=1;j<=m;j++){
			ans-=g[ilem];
			ilem++;
		}
		dp[i]=ans;//dp是答案
	}
	for(cas=1;cas<=T;cas++)
	{
		scanf("%d",&ps);
		printf("%lld\n",dp[ps]);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值