Educational Codeforces Round 108 (Rated for Div. 2) C题

传送门

  • 题意
    在这里插入图片描述

就是给你   n   ~n~  n 个学生,每个学生属于一个大学。
然后问你在每个大学中挑选若干组   k   ~k~  k 个人的队伍参赛,不足k个人的舍去。每个人有一个能力值。问你   k = 1 − n   ~k=1-n~  k=1n 时,所有学校的总能力值是多大?

  • 思路
    反正就是前缀和嘛,把最后模剩下的去掉,把每个队伍的加起来,就是   k = i   ~k=i~  k=i 的答案。然后就疯狂 T T T T T TTTTT TTTTT

  • 大佬思路
    也是这个道理,就是找个一维数组存一下答案,在处理前缀和过程中就算出来。具体看代码

  • 大佬思路的代码(这垃圾代码是我写的)

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;

#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
ll U[N],S[N];
//ll qian[N];ll hou[N];
bool cmp(ll a,ll b){
	return a>b;
}
ll ans[N];
void solve()
{
	int n;
	scanf("%d",&n);
	memset(ans,0*1ll,sizeof(ans));
	map<int,vector<ll> > vec;
	map<int,vector<ll> > qian;
	for(int i=1;i<=n;i++){
		scanf("%lld",&U[i]);
	}
	for(int i=1;i<=n;i++){
		scanf("%lld",&S[i]);
		vec[U[i]].push_back(S[i]);
	}
	//cout<<1<<endl;
	int len_max=0;
	for(auto &item:vec){
		int i=item.first;
		sort(item.second.begin(),item.second.end(),cmp);
	}
	//cout<<1<<endl;
	for(auto item:vec){
		int i=item.first;
		//if(!item.second.size()) continue;
		qian[i].push_back(0*1ll);
		for(int j=0;j<item.second.size();j++){
			qian[i].push_back(qian[i].back()+vec[i][j]);
		}
		int x=vec[i].size();
		for(int j=1;j<=x;j++){
			ans[j]+=qian[i][x/j*j];
		}
	}
	
	for(int i=1;i<=n;i++){
		printf("%lld ",ans[i]);
	}
	printf("\n");
	//cout<<"ok"<<endl;
}
int main()
{
	int t;
	scanf("%d",&t);
	while (t--)
		solve();
}
  • 疯狂tle的代码
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;

#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
ll U[N],S[N];
//ll qian[N];ll hou[N];
bool cmp(ll a,ll b){
	return a>b;
}
void solve()
{
	int n;
	scanf("%d",&n);
	vector<ll> vec[n+1];
	vector<ll> qian[n+1];
	for(int i=1;i<=n;i++){
		scanf("%lld",&U[i]);
	}
	for(int i=1;i<=n;i++){
		scanf("%lld",&S[i]);
		vec[U[i]].push_back(S[i]);
	}
	//cout<<1<<endl;
	int len_max=0;
	for(int i=1;i<=n;i++){
		int t=vec[i].size();
		len_max=max(len_max,t);
		if(vec[i].size())
			sort(vec[i].begin(),vec[i].end(),cmp);
	}
	//cout<<1<<endl;
	for(int i=1;i<=n;i++){
		if(!vec[i].size()) continue;
		qian[i].push_back(0);
		for(int j=0;j<vec[i].size();j++){
			qian[i].push_back(qian[i].back()+vec[i][j]);
		}
	}
	//cout<<1<<endl;
	
	for(int i=1;i<=n;i++){
		ll ans=0;
		if(i>len_max){
			cout<<"0 ";continue;
		}
		for(int j=1;j<=n;j++){
			if(vec[j].size()<i) continue;
			else{
				int x=vec[j].size()%i;
				int len=vec[j].size();
				ans+=qian[j][len-x];
			} 
		}
		printf("%d ",ans);
	}
	printf("\n");
	//cout<<"ok"<<endl;
}
int main()
{
	int t;
	scanf("%d",&t);
	while (t--)
		solve();
}
  • 题意
    在这里插入图片描述

给你   a , b   ~a,b~  a,b 两个数组,然你能不能反转一个连续的子数组,然后使得对应位置的成绩之和最大。

  • 思路
    区间dp(好像还可以暴力做),   f [ l ] [ r ]   ~f[l][r]~  f[l][r] 表示翻转   l − r   ~l−r~  lr 区间之后   ∑ i = l r a i ∗ b i   ~\sum_{i=l}^{r}a_i∗b_i~  i=lraibi 的值,之后再枚举一遍区间对res取一个max即可。
    状态转移方程为:
      f [ i ] [ j ] = f [ i + 1 ] [ j − 1 ] + a [ i ] ∗ b [ j ] + a [ j ] ∗ b [ i ]   ~f[i][j]=f[i+1][j−1]+a[i]∗b[j]+a[j]∗b[i]~  f[i][j]=f[i+1][j1]+a[i]b[j]+a[j]b[i] 
    需要注意的是第一层循环必须是从大到小循环,因为我们会使用到i+1。

  • 代码

const int N = 5010;
 
int n, m, k;
LL a[N], b[N];
LL f[N][N]; // f[l][r] 表示将l-r区间反转之后的值是多少
LL s[N];    
 
int main()
{
    cin >> n;
 
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    for (int i = 1; i <= n; i ++ ) cin >> b[i];
 
    for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i] * b[i];
 
    for (int i = 1; i <= n; i ++ ) f[i][i] = a[i] * b[i]; // 初始化,只翻转一个数
 
    for (int i = n - 1; i >= 1; i -- ) 
        for (int j = i + 1; j <= n; j ++ )
            f[i][j] = f[i + 1][j - 1] + a[i] * b[j] + a[j] * b[i];
 
    LL res = s[n];
    for (int i = 1; i <= n; i ++ ) 
        for (int j = i + 1; j <= n; j ++ ) 
            res = max(res, f[i][j] + s[i - 1] + s[n] - s[j]);
 
    cout << res << endl;
 
    return 0;
}
  • 暴力思路
    应该是枚举长度,枚举起点终点。具体我还没看太懂。
  • 暴力代码
#include <bits/stdc++.h>
using namespace std;

const int maxn = 5010;
int n, a[maxn], b[maxn];
long long ans, pre[maxn], suf[maxn], sum[maxn];

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", &b[i]);
    }
    for (int i = 1; i <= n; i++) {
        pre[i] = pre[i - 1] + 1LL * a[i] * b[i];
    }
    for (int i = n; i; i--) {
        suf[i] = suf[i + 1] + 1LL * a[i] * b[i];
    }
    ans = pre[n];
    for (int i = 2; i <= 2 * n; i++) {
        for (int j = 1; j <= min(n, i); j++) {
            sum[j] = sum[j - 1] + 1LL * a[j] * b[i - j];
        }
        for (int j = 1; j <= n; j++) {
            int k = i - j;
            if (k < j || k > n) continue;
            ans = max(ans, pre[j - 1] + suf[k + 1] + sum[k] - sum[j - 1]);
        }
    }
    printf("%lld\n", ans);
    return 0;
}
  • 官方题解
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂的码泰君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值