[CSP-S 2024] 染色
1.[TK] 矩阵取数游戏<简单版> hzoi-tg-906-22.[TK] 一心净士 hzoj-tg-937-23.[TK] 盖房子 hzoi-tg#2624.[TK] 三角蛋糕 hzoi-tg#2615.[TK] 选课 hzoj-tg#2796.[TK] 三色二叉树 hzoi-tg#282 存图方法7.[TK] HH的项链 离线树状数组解法8.[TK] 理想的正方形9.[TK] Blocks 单调栈10.[TK] 送礼物11.[TK] Terrible Prime12.[TK] BLO13.[TK] Rudolf and Subway ( CodeForces #933 div.3 - G )14.[TK] Bulls And Cows S15.[TK] 颜色16.[Tkey] 与非17.[TK] CF1526B I Hate 111118.[TK] 寻宝游戏19.[CL-22] 异或和之和20.联训题单 / 集训杂题纪要21.[At_dp_w] Intervals & [At_dp_x] Tower22.CF2023D - Many Games23.[NOI2021] 轻重边
24.[CSP-S 2024] 染色
还是决定把这个题做了
考场上设计的状态,推了一个小时没推出来
下午推了一会,发现这是个刷表状态,填表没法做,转移无处下手
但是考 CSP 的时候我貌似并不知道什么叫刷表
设
#include<bits/stdc++.h>
using namespace std;
int n;
int a[200001];
int f[102][101][101];
int ans=0;
int main(){
ios::sync_with_stdio(false);
int cases;cin>>cases;while(cases--){
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}
ans=0;
memset(f,0,sizeof f);
for(int i=1;i<=n;++i){
for(int j=0;j<i;++j){
for(int k=0;k<i;++k){
if(j!=i-1 and k!=i-1) continue;
f[i+1][j][i]=max(f[i+1][j][i],f[i][j][k]+(a[k]==a[i]?a[i]:0));
f[i+1][i][k]=max(f[i+1][i][k],f[i][j][k]+(a[j]==a[i]?a[i]:0));
ans=max({ans,f[i+1][j][i],f[i+1][i][k]});
}
}
}
cout<<ans<<'\n';
}
}
lbtl 说,既然都刷表了,为什么不写记搜呢,一语惊醒梦中人
在写记搜的同时也发现,由于
记搜
#include<bits/stdc++.h>
using namespace std;
int n;
int a[10001];
int f[2001][2001];
int dfs(int now,int r,int b){
if(now>n) return 0;
if(f[r][b]!=-1) return f[r][b];
return f[r][b]=max(dfs(now+1,now,b)+(a[now]==a[r]?a[now]:0),dfs(now+1,r,now)+(a[now]==a[b]?a[now]:0));
}
int main(){
ios::sync_with_stdio(false);
int cases;cin>>cases;while(cases--){
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}
memset(f,-1,sizeof f);
cout<<dfs(1,0,0)<<'\n';
}
}
刷表法
#include<bits/stdc++.h>
using namespace std;
int n;
int a[200001];
int f[2001][2001];
int ans=0;
int main(){
ios::sync_with_stdio(false);
int cases;cin>>cases;while(cases--){
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}
ans=0;
memset(f,0,sizeof f);
for(int j=0;j<n;++j){
for(int k=0;k<n;++k){
f[j][max(j,k)+1]=max(f[j][max(j,k)+1],f[j][k]+(a[k]==a[max(j,k)+1]?a[max(j,k)+1]:0));
f[max(j,k)+1][k]=max(f[max(j,k)+1][k],f[j][k]+(a[j]==a[max(j,k)+1]?a[max(j,k)+1]:0));
ans=max({ans,f[j][max(j,k)+1],f[max(j,k)+1][k]});
}
}
cout<<ans<<'\n';
}
}
写到这里已经 210 分了,考场上的我到底在干什么呢
这个做法空间压不下来,已经没前途了,想做正解还要另辟蹊径
返璞归真,设
考虑从
如果
证明:为了有贡献,
因此,将贡献拆成三段
,由 可以知道 ,这是都涂同一种颜色的连续段 ,刚才我们钦定 有贡献,这个贡献就是
这个东西需要处理细节,分别讨论
这么分是因为,由于离
现在复杂度瓶颈是第二个连续段,这一段可前缀和处理,因此上前缀和
复杂度线性
这题也有坑点
// if(last[a[i]]) f[i]=max(f[i-1],f[last[a[i]]+1]+sum[i]-sum[last[a[i]]+1]+a[i]);
// else f[i]=f[i-1];
// last[a[i]]=i;
不能这么写是因为 last[a[i]]+1
有可能直接等于 i
了,因此需要提前更新 i
的值
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int a[1000001];
int last[1000001];
int f[1000001];
int sum[1000001];
signed main(){
ios::sync_with_stdio(false);
int cases;cin>>cases;while(cases--){
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
last[a[i]]=0;
sum[i]=sum[i-1]+(a[i]==a[i-1]?a[i]:0);
}
for(int i=1;i<=n;++i){
f[i]=f[i-1];
if(last[a[i]]) f[i]=max(f[i],f[last[a[i]]+1]+sum[i]-sum[last[a[i]]+1]+a[i]);
last[a[i]]=i;
}
cout<<f[n]<<'\n';
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!