CF521D Shop
又是神秘的一题。
题意
题意就是给你 \(k\) 个技能,有 \(n\) 个可选操作,需要选 \(m\) 个操作使总和最大。
\(t\) :操作类别
\(i\) :操作对象是第几个技能
\(b\) :按 \(t\) 分3类
操作有3类:
\(t\) ==1时,把第 \(i\) 个技能变成 \(b\) ;
\(t\) ==2时,把第 \(i\) 个技能加上 \(b\) ;
\(t\) ==3时,把第 \(i\) 个技能乘上 \(b\) 。
解法
从局部开始思考,如果只有乘法,那就直接把 \(b\) 从大到小排序,选前 \(m\) 个就行了。再深挖一点,如果是有加法和乘法两种呢?那就考虑把加法转乘法,就是假设 \(b\) 现在是加上 \(b\) 的意思,那转为乘法就应该是 \(\frac{a_{i}+b}{a_{i}}\) (分数有点小)
最最最最最后,思考赋值的操作(就是把第 \(i\) 个技能变成 \(b\)),思考一下,就可以发现我们能把赋值转为加法,那不就完成了吗?
一点细节:
1.计算顺序因改为赋值到加法到乘法,这个很好思考吧。
2.如果一个数有多次赋值操作,那就保留赋值大的(就是 \(b\) 大的),因为如果这两次都赋值,那为什么不一次就赋值最大的那个呢?而且你是把赋值转为加法,如果一个数多次赋值,那转为加法就会有一些地方会被重复加。
代码
码风极为丑陋,请勿学习!如要借用,请改成你的精美码风!
有注释版
**#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1e5+5;
// 结构体node用于存储最终的乘法因子(包括原始乘法和转化后的加法/赋值)
struct node
{
double x; // 乘法因子(即操作对乘积的放大倍数)
ll t,id; // t:操作类型(1赋值,2加法,3乘法), id:操作编号
}e[N];
// 结构体Node用于存储最终选中的操作,用于排序输出
struct Node
{
ll id,t; // id:操作编号, t:操作类型
}ans[N];
// 结构体edge用于存储每个技能的加法操作(包括原始加法和转化后的赋值)
struct edge
{
ll t,x,id; // t:操作类型, x:加法的数值(赋值操作转化为差值), id:操作编号
};
vector<edge>c[N]; // c[i]存储技能i的所有加法操作(按数值降序使用)
ll a[N]; // 各技能的当前值(动态更新)
ll ecnt; // 最终乘法因子数组e的元素个数
ll Max[N],Pos[N]; // Max[i]记录技能i的赋值操作中的最大值,Pos[i]记录对应操作编号
ll acnt; // 最终选中的操作数量
// 比较函数:按乘法因子降序排序
bool cmp(node x,node y){return x.x>y.x;}
// 比较函数:按加法数值降序排序
bool cmp1(edge x,edge y){return x.x>y.x;}
// 比较函数:先按操作类型升序(1赋值,2加法,3乘法),再按编号升序
bool cmp2(Node x,Node y){if (x.t!=y.t){return x.t<y.t;} return x.id<y.id;}
signed main()
{
std::ios::sync_with_stdio(false); std::cin.tie(nullptr);
ll k,n,m; cin >>k>>n>>m;
for (ll i=1;i<=k;i++) cin >>a[i]; // 读入各技能初始值
// 读入n个操作
for (ll _=1;_<=n;_++)
{
ll t,i,b; cin >>t>>i>>b;
if (t==1) // 赋值操作
{
// 对于每个技能,只保留最大的赋值,因为小的赋值不如大的
if (b>Max[i]) Max[i]=b,Pos[i]=_;
}
if (t==2) {c[i].push_back({t,b,_});} // 加法操作存入对应技能的vector
if (t==3) {e[++ecnt].x=1.0*b; e[ecnt].t=t,e[ecnt].id=_;} // 乘法操作直接存入全局数组
}
// 处理赋值操作:如果最大赋值比当前值大,则将其转化为一个加法(差值)
for (ll i=1;i<=k;i++)
{
if (Max[i] && Max[i]>a[i]) c[i].push_back({1,Max[i]-a[i],Pos[i]});
}
// 对每个技能的加法操作(包括原始加法和转化来的赋值)按数值降序排序
for (ll i=1;i<=k;i++)
{
sort(c[i].begin(),c[i].end(),cmp1);
// 依次应用加法(按降序),每次应用后更新a[i],并将该操作转化为乘法因子存入e
for (auto j:c[i])
{
int t=j.t,x=j.x,id=j.id;
// 转化:应用此加法后,技能值变为 a[i]+x,所以乘数为 (a[i]+x)/a[i]
e[++ecnt].x=1.0*(x+a[i])/a[i];
e[ecnt].t=t;
e[ecnt].id=id;
a[i]+=x; // 更新当前值,以便后续加法基于新值计算乘数
}
}
// 将所有乘法因子(包括原始乘法和转化后的加法)按降序排序
sort(e+1,e+ecnt+1,cmp);
// 选取前 min(ecnt, m) 个最大的乘法因子作为最终选中的操作
for (ll i=1;i<=min(ecnt,m);i++) {ans[++acnt].id=e[i].id; ans[acnt].t=e[i].t;}
// 对选中的操作排序:先赋值,再加法,最后乘法(保证应用顺序正确)
sort(ans+1,ans+acnt+1,cmp2);
// 输出选中的操作数量及其编号(按正确顺序)
cout <<acnt<<endl;
for (ll i=1;i<=acnt;i++) cout <<ans[i].id<<" ";
return 0;
}
无注释版
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1e5+5;
struct node
{
double x;
ll t,id;
}e[N];
struct Node
{
ll id,t;
}ans[N];
struct edge
{
ll t,x,id;
};
vector<edge>c[N];
ll a[N],ecnt,Max[N],Pos[N],acnt;
bool cmp(node x,node y){return x.x>y.x;}
bool cmp1(edge x,edge y){return x.x>y.x;}
bool cmp2(Node x,Node y){if (x.t!=y.t){return x.t<y.t;} return x.id<y.id;}
signed main()
{
std::ios::sync_with_stdio(false); std::cin.tie(nullptr);
ll k,n,m; cin >>k>>n>>m;
for (ll i=1;i<=k;i++) cin >>a[i];
for (ll _=1;_<=n;_++)
{
ll t,i,b; cin >>t>>i>>b;
if (t==1)
{
if (b>Max[i]) Max[i]=b,Pos[i]=_;
}
if (t==2) {c[i].push_back({t,b,_});}
if (t==3) {e[++ecnt].x=1.0*b; e[ecnt].t=t,e[ecnt].id=_;}
}
for (ll i=1;i<=k;i++)
{
if (Max[i] && Max[i]>a[i]) c[i].push_back({1,Max[i]-a[i],Pos[i]});
}
for (ll i=1;i<=k;i++)
{
sort(c[i].begin(),c[i].end(),cmp1);
for (auto j:c[i])
{
int t=j.t,x=j.x,id=j.id;
e[++ecnt].x=1.0*(x+a[i])/a[i]; e[ecnt].t=t,e[ecnt].id=id;
a[i]+=x;
}
}
sort(e+1,e+ecnt+1,cmp);
for (ll i=1;i<=min(ecnt,m);i++) {ans[++acnt].id=e[i].id; ans[acnt].t=e[i].t;}
sort(ans+1,ans+acnt+1,cmp2);
cout <<acnt<<endl;
for (ll i=1;i<=acnt;i++) cout <<ans[i].id<<" ";
return 0;
}

浙公网安备 33010602011771号