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

CF::Gym题目页面传送门

\(n\)组任务,每组\(m_i\)个,第\(i\)组第\(j\)个任务的编号为\(\sum\limits_{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_{1\sim x-1},ord_{x+2\sim m_i}\)对总处罚的贡献和不会因为\(ord_x,ord_{x+1}\)交换而变化,我们只需要关心\(ord_x,ord_{x+1}\)对总处罚的贡献和。设\(sum=\sum\limits_{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}\right]\),这样一来,比较器\(cmp\)的合法性显然。此时,“没有相邻逆序对的顺序一定是最优的”这个结论正确性也很显然了:排完序后,若\(\dfrac{a_x}{b_x}=\dfrac{a_y}{b_y}\),则\(x\)\(y\)一定相邻,任意\(2\)个有序的顺序一定可以通过重新排列每个相等段互相到达,此时易证所有有序顺序的总处罚相等,得证。

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

下面是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<<"\n";//输出答案 
    for(int i=0;i<n;i++)for(int j=0;j<m[v[i]];j++)cout<<ord[v[i]][j]<<" ";//输出顺序 
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ycx-akioi/p/CF-Gym-100113K.html