CF::Gym 100113K - The Merry Student Life During the Term. . .

Posted ycx-akioi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF::Gym 100113K - The Merry Student Life During the Term. . .相关的知识,希望对你有一定的参考价值。

CF::Gym题目页面传送门

(n)组任务,每组(m_i)个,第(i)组第(j)个任务的编号为(sumlimits_{k=1}^{i-1}m_k+j)。第(i)个任务需要(a_i)个单位时间完成。刚开始时刻为(0),每完成一个任务(i)当前的时刻就增加(a_i)。若第(i)个任务完成的时刻为(t),则受到的处罚为(b_it)。从开始做一个任务到结束不允许做其他任务,从开始做某组任务到结束不允许做其他组的任务。求一个最优的完成任务顺序,使得受到的总处罚最小。输出最小总处罚和顺序。若有多解,输出任一。

对于任何一个任务组的顺序,显然在此顺序下使得总处罚最小的每组组内顺序是不变的。于是考虑先求出每组组内最优顺序,再求任务组的最优顺序。

像这种求最优顺序的题,往往都有一个套路。对于每组(i),考虑交换任意一种顺序(ord)下任意一个相邻对((ord_x,ord_{x+1})),使它变成((ord_{x+1},ord_x)),会不会更优。显然,(ord_{1sim x-1},ord_{x+2sim m_i})对总处罚的贡献和不会因为(ord_x,ord_{x+1})交换而变化,我们只需要关心(ord_x,ord_{x+1})对总处罚的贡献和。设(sum=sumlimits_{j=1}^{x-1}a_{ord_j}),则(ord_x,ord_{x+1})顺序下它们对总处罚的贡献和为(b_{ord_x}(sum+a_{ord_x})+b_{ord_{x+1}}(sum+a_{ord_x}+a_{ord_{x+1}})=b_{ord_x}sum+b_{ord_x}a_{ord_x}+b_{ord_{x+1}}sum+b_{ord_{x+1}}a_{ord_x}+b_{ord_{x+1}}a_{ord_{x+1}}=(b_{ord_x}+b_{ord_{x+1}})sum+a_{ord_x}b_{ord_x}+a_{ord_{x+1}}b_{ord_{x+1}}+a_{ord_x}b_{ord_{x+1}});类似地,可以求出(ord_{x+1},ord_x)顺序下对总处罚的贡献和为((b_{ord_x}+b_{ord_{x+1}})sum+a_{ord_x}b_{ord_x}+a_{ord_{x+1}}b_{ord_{x+1}}+a_{ord_{x+1}}b_{ord_x})。当((b_{ord_x}+b_{ord_{x+1}})sum+a_{ord_x}b_{ord_x}+a_{ord_{x+1}}b_{ord_{x+1}}+a_{ord_x}b_{ord_{x+1}}>(b_{ord_x}+b_{ord_{x+1}})sum+a_{ord_x}b_{ord_x}+a_{ord_{x+1}}b_{ord_{x+1}}+a_{ord_{x+1}}b_{ord_x}),即(a_{ord_x}b_{ord_{x+1}}>a_{ord_{x+1}}b_{ord_x})时,显然可以交换(ord_x,ord_{x+1})得到更优策略。我们将这样在一个顺序内可以交换使得策略更优的相邻对称为“相邻逆序对”。

显然,有相邻逆序对的顺序一定不是最优的。我们暂且认为没有相邻逆序对的顺序一定是最优的。于是我们定义一个比较器(cmp(x,y)=[a_xb_y<a_yb_x]),按照它将该组内所有元素排序,这样得到的顺序一定是最优的。([a_xb_y<a_yb_x])可以变形成(left[dfrac{a_x}{b_x}<dfrac{a_y}{b_y} ight]),这样一来,比较器(cmp)的合法性显然。此时,“没有相邻逆序对的顺序一定是最优的”这个结论正确性也很显然了:排完序后,若(dfrac{a_x}{b_x}=dfrac{a_y}{b_y}),则(x)(y)一定相邻,任意(2)个有序的顺序一定可以通过重新排列每个相等段互相到达,此时易证所有有序顺序的总处罚相等,得证。

求出了每组组内的最优顺序,现在来求任务组的最优顺序。上面是默认每组的开始时间为(0)的,而现在不一定。设(sum\_a_i=sumlimits_{j=sumlimits_{k=1}^{i-1}m_k+1}^{sumlimits_{k=1}^im_k}a_j,sum\_b_i=sumlimits_{j=sumlimits_{k=1}^{i-1}m_k+1}^{sumlimits_{k=1}^im_k}b_j)。对于一个顺序(ord),第(ord_i)组的开始时间显然是(sumlimits_{j=1}^{i-1}sum\_a_i),这比开始时间为(0)对总处罚的贡献增加了(sum\_b_{ord_i}sumlimits_{j=1}^{i-1}sum\_a_{ord_j})。于是,我们可以把每个任务组看成一个大任务,按照与上面类似的方法定义一个比较器(cmp'(x,y)=left[sum\_a_xsum\_b_y<sum\_a_ysum\_b_x ight])并按照它将所有任务组排序,即可得到最优顺序。

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
const int N=500,M_I=100;
int n;//任务组数 
int m[N+1];//每组任务个数 
int a[N+1][M_I+1]/*所需时间*/,b[N+1][M_I+1]/*处罚系数*/;
int sum_a[N+1]/*该组内所有任务所需时间之和*/,sum_b[N+1]/*该组内所有任务处罚系数之和*/;
int now;//当前正在处理的任务组 
bool cmp(int x,int y){return a[now][x]*b[now][y]<a[now][y]*b[now][x];}//给组内任务排序的比较器 
bool cmp0(int x,int y){return sum_a[x]*sum_b[y]<sum_a[y]*sum_b[x];}//给任务组排序的比较器 
vector<int> ord[N+1]; 
signed main(){
//  freopen("student.in","r",stdin);freopen("student.out","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>m[i];
    for(int i=1;i<=n;i++)for(int j=1;j<=m[i];j++)cin>>a[i][j];
    for(int i=1;i<=n;i++)for(int j=1;j<=m[i];j++)cin>>b[i][j];
    int ans=0;
    int cnt=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m[i];j++)sum_a[i]+=a[i][j],sum_b[i]+=b[i][j];//计算sum_a,sum_b 
        vector<int> v(m[i]); 
        for(int j=1;j<=m[i];j++)v[j-1]=j;
        now=i;
        sort(v.begin(),v.end(),cmp);//排序 
        int tim=0;
        for(int j=0;j<m[i];j++)ans+=b[i][v[j]]*(tim+=a[i][v[j]]);//贡献答案 
        for(int j=0;j<m[i];j++)ord[i].pb(cnt+v[j]);//记录最优顺序 
        cnt+=m[i];
    }
    vector<int> v(n);
    for(int j=1;j<=n;j++)v[j-1]=j;
    sort(v.begin(),v.end(),cmp0);//排序 
    int tim=0;
    for(int i=0;i<n;i++)ans+=tim*sum_b[v[i]],tim+=sum_a[v[i]];//贡献答案 
    cout<<ans<<"
";//输出答案 
    for(int i=0;i<n;i++)for(int j=0;j<m[v[i]];j++)cout<<ord[v[i]][j]<<" ";//输出顺序 
    return 0;
}

以上是关于CF::Gym 100113K - The Merry Student Life During the Term. . .的主要内容,如果未能解决你的问题,请参考以下文章

cf gym gym 101177 A - Anticlockwise Motion

CF Gym Dice Game BFS 暴搜

cf gym 100960 G. Youngling Tournament set+树状数组

cf gym 101124

CF Gym-101911A Coffee Break

CF Gym 102059E Electronic Circuit (set存图删点)