- 题意
就是给你 n ~n~ n 个学生,每个学生属于一个大学。
然后问你在每个大学中挑选若干组 k ~k~ k 个人的队伍参赛,不足k个人的舍去。每个人有一个能力值。问你 k = 1 − n ~k=1-n~ k=1−n 时,所有学校的总能力值是多大?
-
思路
反正就是前缀和嘛,把最后模剩下的去掉,把每个队伍的加起来,就是 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~ l−r 区间之后 ∑ i = l r a i ∗ b i ~\sum_{i=l}^{r}a_i∗b_i~ ∑i=lrai∗bi 的值,之后再枚举一遍区间对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][j−1]+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;
}
- 官方题解