AtCoder Beginner Contest 257 Ex
这么难的题出在ABC真的好吗?
首先是一波推式子,假设我们选的骰子集合为(这里储存的是骰子的下标),那么我们不难列出下面的式子:
考虑前半部分很烦,我们可以拆掉平方,即利用完全平方公式
可以得到:
我们发现,对于,的贡献是(相当于固定了这一位,其它个骰子可以在到中任意选择),而的贡献是(相当于固定这位,其它个骰子任意选择)
所以,我们就可以愉快的把最外面,也是最难枚举的给去除了!
将乘进去:
我们发现:
(相当于两两组合,但是每一对要被计算两遍:在和时都会被计算,故要除以,并且还要减去自己和自己组合的情况)
那么我们设,,可得:
设。
则:
可以在输入时就计算好。那么现在我们就将原问题转成如下的问题:
给你个数对,让你选出个数对,使得最大。
此时我们可以得到一个非常重要的结论:
若我们选择集合最优,那么一定存在一个整数,满足对于任意,。
证明:
咕咕咕(马上补)
那么此时我们就可以枚举整数从到,找到当前最大的个,将更新到答案。
此时时间复杂度为,虽然不能AC,但是已经不是一个指数级别的做法了。我们已经有了很大的进步。
其实还可以优化。我们利用类似离散化的思想:如果时刻的的数列和时刻的不变,那么我们没有必要枚举时刻。所以,数列只会在从某对从变成时改变。即时改变。那么我们把这个时刻存储下来,枚举等于它们的时候,以及最初()时即可。时间复杂度为。
继续优化!我们发现上面做法慢的原因在于每次枚举新的时,总是要对数组重新排序。如果我们可以利用单调性维护排序数组呢?我们发现,当时,我们只改变的大小关系,也就是说对于之前的数组,我们只要交换就可以得到现在的数组。故我们就可以先排序,之后每枚举一个,交换对应的下标即可,时间复杂度为。
代码如下:
#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl
using ll=long long;
const int maxn=1005;
const ll inf=4e18;
int n,k;
int c[maxn],a[maxn][10],id[maxn];
ll s[maxn],t[maxn],p[maxn];
ll calc(int ind) {
int x=id[ind],y=id[ind+1];
if(s[x]>=s[y]) return inf;
return (t[x]-t[y]+s[y]-s[x]-1ll)/(s[y]-s[x]);
}
int main() {
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=6;j++)
scanf("%d",&a[i][j]);
ll Ss=0,St=0,ans=-inf;
for(int i=1;i<=n;i++) {
for(int j=1;j<=6;j++) {
s[i]+=1ll*a[i][j];
p[i]+=1ll*a[i][j]*a[i][j];
}
t[i]=6ll*p[i]-s[i]*s[i]-36ll*c[i];
id[i]=i;
}
std::sort(id+1,id+n+1,[](int id1,int id2){return t[id1]>t[id2];});
for(int i=1;i<=k;i++) Ss+=s[id[i]],St+=t[id[i]];
ans=std::max(ans,Ss*Ss+St);
std::set<std::pair<long long,int>> S;
for(int i=1;i<n;i++) S.insert({calc(i),i});
while(!S.empty()) {
std::pair<long long,int> rec=*S.begin();
if(rec.first==inf) break;
S.erase({calc(rec.second),rec.second});
if(rec.second>=2) {
S.erase({calc(rec.second-1),rec.second-1});
}
if(rec.second<=n-2) {
S.erase({calc(rec.second+1),rec.second+1});
}
if(rec.second==k) {
Ss-=s[id[rec.second]]; St-=t[id[rec.second]];
}
std::swap(id[rec.second],id[rec.second+1]);
S.insert({calc(rec.second),rec.second});
if(rec.second>=2) {
S.insert({calc(rec.second-1),rec.second-1});
}
if(rec.second<=n-2) {
S.insert({calc(rec.second+1),rec.second+1});
}
if(rec.second==k) {
Ss+=s[id[rec.second]]; St+=t[id[rec.second]];
}
ans=std::max(ans,Ss*Ss+St);
}
//注意ans还有可能为负数
ans=(ans%998244353+998244353)%998244353;
printf("%lld\n",ans*859599304ll%998244353ll);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话