题解:P14174 【MX-X23-T4】卡常数
题解:P14174 【MX-X23-T4】卡常数
思路
首先有一个贪心,对于每个序列我们选择减去的比例尽量多的数是更优的,比如以下一个例子:
1 2
3 1
2 100 5
1 30 2
此时三个数减去的比例分别是 12,310,25\frac{1}{2},\frac{3}{10},\frac{2}{5}21,103,52,选择第 1 和 3 个数减去是最优的,答案为 300,如果减去第 2 和 3 个数,则答案为 350,并不是最优的。
接下来我们将每个序列按此规则排序,减去比例越多则越靠前。
设 fi,jf_{i,j}fi,j 表示第 iii 个序列减去 jjj 个数后的答案,此处选择的 jjj 个数根据贪心是排序后的前 jjj 个数,再设 gi,jg_{i,j}gi,j 表示第 iii 个序列减去 j−1j-1j−1 个数后的答案和减去 jjj 个数后的答案的差,方便我们计算答案。
接下来先将所有 fi,0f_{i,0}fi,0 加起来,接下来每次选择 gi,jg_{i,j}gi,j 最大的一个序列,将答案减去 gi,jg_{i,j}gi,j,使用优先队列维护,此处可能有些难理解,可以结合代码理解。
代码
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef __int128 ll;
long long n,k,x[500010],l[500010];
struct N{ll a,b;
};
vector<N> v[500010];
bool cmp(N a,N b){return a.b*b.a<b.b*a.a;
}
ll pre[500010],suf[500010],*f[500010],*g[500010];
struct E{int x,v;bool operator<(const E &n1)const{//按照g[i][j]排序,每次选择g[i][j]最大的序列 return g[x][v]<g[n1.x][n1.v];}
};
void pr(ll x){if(x/10)pr(x/10);putchar(x%10+'0');
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>k;for(int p=1;p<=n;p++){cin>>x[p]>>l[p];f[p]=new ll[l[p]+5];g[p]=new ll[l[p]+5];for(int i=1;i<=l[p];i++){long long x;cin>>x;v[p].push_back({x,0});}for(int i=1;i<=l[p];i++){long long b;cin>>b;v[p][i-1].b=v[p][i-1].a-b;//此处储存的是减去后剩余的比例 }sort(v[p].begin(),v[p].end(),cmp);//给比例排序 pre[0]=1;for(int i=1;i<=l[p];i++){pre[i]=pre[i-1]*v[p][i-1].b;//pre[i]表示前i个被剪的数的答案 }suf[l[p]+1]=1;for(int i=l[p];i>=1;i--){suf[i]=suf[i+1]*v[p][i-1].a;//suf[i]表示i后的没有被减的数的答案 }for(int i=0;i<=l[p];i++){f[p][i]=pre[i]*suf[i+1]*x[p];//即前i个被减去,后面的没有被减去,记得乘x[i] if(i>0)g[p][i]=f[p][i-1]-f[p][i];//计算g[i][j] }}ll ans=0;priority_queue<E> q;for(int i=1;i<=n;i++){q.push({i,1});ans+=f[i][0];//先将不减数的结果存下来 }while(!q.empty()&&k--){E t=q.top();//每次选择当前最大的g[i][j]减 q.pop();ans-=g[t.x][t.v];if(t.v<l[t.x])q.push({t.x,t.v+1});//减去后要再将下一个g[i][j]放入优先队列 }pr(ans);return 0;
}