CF521D

CF521D

有 k 个正整数 a1…k​。
有 n 个操作,每个操作给定正整数 b,有三种可能:将 ai​ 赋值为 b,将 ai​ 加上 b,将 ai​ 乘以 b。
你可以从 n 个操作中选择最多 m 个操作,并按照一定顺序执行。
你的目标是最大化这些 ai​ 的乘积。
k,n ≤ 1e5。
输出操作个数和操作顺序

=============================================================

对任意一个数的乘法操作都可以直接对整体做贡献,所以将赋值和加法转化为乘法进行比较每个操作对答案的贡献

其它两个操作用贪心考虑,对于同一个数,最多使用一次赋值操作,且该操作需满足赋值后大于原值;而对于加法来说,在尽量少的次数中尽量保证加了尽量大的数。

将赋值转化为加法运算,再将所有加法运算进行比较,同一个数加得多的先操作

然后把加法转化为乘法操作,(原理 :a + b = a * ((a + b) / a))。

最后的操作序列就全是乘法操作了,只需取前 m 次操作即可

code

typedef pair <int,int> PII;
typedef pair <long double,int> PLI
PII as[N];
vector <PII> add[N];
vector <PLI> mul;
 
bool cmp(PII a,PII b){return t[a.second] < t[b.second];}
 
int main()
{
    cin >> n >> k >> m;
    for(int i = 1;i <= n;i ++) cin >> a[i];
    for(int i = 1;i <= k;i ++)
    {
        int x,y,z;
        cin >> x >> y >> z;
        t[i] = x;//保存操作顺序,最后先输出 操作1 , 2 , 3; 
        if(x == 1) as[y] = max(as[y],{z,i});
        if(x == 2) add[y].push_back({z,i});
        if(x == 3) mul.push_back({z,i});
    }
    for(int i = 1;i <= n;i ++)
        if(as[i].first > a[i])
            add[i].push_back({as[i].first - a[i],as[i].second});//将所有赋值操作转化为加法操作 
    for(int i = 1;i <= n;i ++)
    {
        sort(add[i].begin(),add[i].end());
        reverse(add[i].begin(),add[i].end());
        //对于加法来说,从大数开始加更优 
        long long v = a[i];
        for(int j = 0;j < add[i].size();j ++)
        {
            PII p = add[i][j];
            mul.push_back({1.0L * (v + p.first) / v,p.second});
            v += p.first;//要记录上一次加完后值变为多少,这样才能转化为乘法 
        }
    }
    sort(mul.begin(),mul.end());
    reverse(mul.begin(),mul.end());
    int x = min(m,(int)mul.size());
    sort(mul.begin(),mul.begin() + x,cmp);//保证赋值和加法操作在先 
    cout << x << endl;
    for(int i = 0;i < x;i ++)
        cout << mul[i].second << ' ';
    return 0;
}

这里贴出思路和代码来源

posted @ 2020-12-02 11:46  星&夜  阅读(90)  评论(0编辑  收藏  举报