C. Berland Regional
题意:一共2e5个学生,告诉你他们的学校和能力。设k表示一个队伍的人数,一个学校可以派出任意个队伍。问当k从1至n时,所有学校能派出最大能力之和。
题解:考虑暴力解法,先将学生放入学校的vector排序,枚举k与学校,易知学校人数不能整除k时,将会有余数个人不能加入和,这里用前缀和优化一下即可。但目前还是个n方的解法,这里我们剪枝一下。当某个学校人数小于k,这个学校就我不想再枚举。这里我们对学校按人数排序,用一个单指针记入第一个学校,当第一个学校人数小于k时,就使指针往后移。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define ll long long
const ll N=2e5+7;
ll t,n,a[N],b[N],sum[N];
vector<ll>ho[N];
pair<ll,ll>pos[N];
bool cmp(ll a,ll b){
return a>b;
}
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
ho[i].clear();
sum[i]=0;
}
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%lld",&b[i]);
}
for(int i=1;i<=n;i++){
ho[a[i]].push_back(b[i]);
}
for(int i=1;i<=n;i++){
sort(ho[i].begin(),ho[i].end(),cmp);
}
for(int i=1;i<=n;i++){
ll len=ho[i].size();
pos[i].first=len;
pos[i].second=i;
for(int j=1;j<len;j++){
ho[i][j]+=ho[i][j-1];
}
}
sort(pos+1,pos+1+n);
int p=1;
for(int i=1;i<=n;i++){
while(p<=n&&pos[p].first<i){
p++;
}
for(int j=p;j<=n;j++){
ll len=pos[j].first;
ll now=pos[j].second;
ll to=len-len%i;
sum[i]+=ho[now][to-1];
}
}
for(int i=1;i<=n;i++){
printf("%lld ",sum[i]);
}puts("");
}
}
D. Maximum Sum of Products
题意:给一个数组a,与数组b,你可以倒置a中一段,求最大的ai*bi之和。
题解:这种题目,很明显不能贪心,所以要暴力求解。枚举a中的每一段,然后每一段由于会倒置,所以贡献要重新算,这样的复杂度是O(n * n * n),我们发现每一段的贡献可以dp算出来,所以时间降至n方。
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
const ll N=5007;
ll n,a[N],b[N],px[N];
ll f[N][N];
ll dfs(ll l,ll r){
if(l==r){
return a[l]*b[l];
}
else if(l+1==r){
return a[l]*b[r]+a[r]*b[l];
}
if(f[l][r]!=-1){
return f[l][r];
}
return f[l][r]=a[l]*b[r]+a[r]*b[l]+dfs(l+1,r-1);
}
int main(){
memset(f,-1,sizeof(f));
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%lld",&b[i]);
px[i]=px[i-1]+a[i]*b[i];
}
ll ans=0;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
ans=max(ans,px[i-1]+px[n]-px[j]+dfs(i,j));
}
}
printf("%lld
",ans);
}