CodeForces 521D Shop

链接:http://codeforces.com/problemset/problem/521/D

神级贪心题,显然对于每一位要先改成能改的最大的,然后从大到小加上这一位能加的,那么加的东西就可以视为能加的某一前缀,这样可以把加ak(即这一位能加的第k个数)看成是乘上sigma(a1+ak)/sigma(a1~ak-1),这样就把加法转换成乘法了!!而修改其实也可以看成加上一个(改后-初始)的值,而乘某个数的话因为答案就是要求所有数乘积最大对于任意位都是等价的,所以直接sort取前m个就可以了。

trick:1.分数运算会爆long long,所以可以每个分数减去1,可以保证运算在long long内且不影响结果。

           2.可能有负的(即修改时改的数比原来小),枚举到这里时break(不一定满的m个)

           3.输出要按操作顺序,即先改再加再乘

#pragma GCC optimize(3,"inline","Ofast")
#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
using namespace std;
const int N=100010;
struct fen{
	ll mom,son;int idx,opt;
	fen(){son=0,mom=0,idx=0,opt=0;}
	fen(ll mo,ll ha,int gg,int op):son(mo),mom(ha),idx(gg),opt(op){}
};
vector<fen>mul;vector<pii>add[N];vector<pii>ans;
int k,n,m,a[N],cg[N],cps[N];
bool operator <(fen x,fen y)
{return x.son*y.mom<y.son*x.mom;}
bool cmp(pii x,pii y)
{return x.first>y.first;}
bool f__k(fen x,fen y)
{return x.opt<y.opt;}
int main()
{
	int t,id,b;ll tmp;
	scanf("%d%d%d",&k,&n,&m);
	for(int i=1;i<=k;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&t,&id,&b);
		if(t==1)
		{
			if(cg[id]<b)cg[id]=b,cps[id]=i;
		}
		else if(t==2)add[id].push_back(pii(b,i));
		else mul.push_back(fen(b-1,1,i,3));
	}
	for(int i=1;i<=k;i++)
	{
		if(cg[i]!=0)
		{
			add[i].push_back(pii(cg[i]-a[i],cps[i]));
		}
		sort(add[i].begin(),add[i].end(),cmp);
		tmp=a[i];
		for(int j=0;j<add[i].size();j++)
		{
			if(add[i][j].second==cps[i])mul.push_back(fen(add[i][j].first,tmp,add[i][j].second,1));
			else mul.push_back(fen(add[i][j].first,tmp,add[i][j].second,2));
			tmp+=add[i][j].first;
		}
	}
	sort(mul.begin(),mul.end());
	tmp=0;
	for(int i=mul.size()-1;i>=0;i--)
	{
		if(tmp==m)break;
		if(mul[i].son>0)
		{
			ans.push_back(pii(mul[i].opt,mul[i].idx));
			tmp++;
		}
		else break;
	}
	sort(ans.begin(),ans.end());
	printf("%lld\n",tmp);
	for(int i=0;i<ans.size();i++)
		printf("%d ",ans[i].second);
}

猜你喜欢

转载自blog.csdn.net/caoyang1123/article/details/81513090