[CSP-S 2024] 染色 题解
[CSP-S 2024] 染色 题解
知识点
动态规划,数据结构,动态规划优化。
题意简述
……(这个还是算了)
分析
一个决策问题,显然的最值背包 DP 解决。
\(50\%\)
考虑暴力,设 \(f_{i,j}\) 表示到第 \(i\) 位,上一个异色的数字是 \(j\),最大得分是多少。
那么转移方程十分简单:
\[f_{i,j} = f_{i-1,j} + a_i [a_i = a_{i-1}] \\
f_{i,a_{i-1}} = \max_{j} (f_{i-1,j} + a_i [a_i = j]) \\
\]
namespace Subtask1 {
const int N(2e3+10);
ll ans;
ll f[N][N];
bool Check() {
return n<=2000;
}
int Cmain() {
RCL(f[0],-0x3f,f[0],1),f[0][0]=0,ans=0;
FOR(i,1,n) {
RCL(f[i],-0x3f,f[i],1);
FOR(j,0,b[0]) {
tomax(f[i][j],f[i-1][j]+(a[i-1]==a[i]?b[a[i]]:0));
tomax(f[i][a[i-1]],f[i-1][j]+(j==a[i]?b[a[i]]:0));
}
}
FOR(j,0,b[0])tomax(ans,f[n][j]);
wr(ans);
return 0;
}
}
\(100\%\)
数据结构优化:线段树
一看上面的形式就可以用线段树优化,只需实现:单点改变最大值,单点查询,全局加,全局取最大值。
然后随便改一下即可。
时间复杂度:\(O(Tn\log_2{n})\),空间复杂度:\(O(n)\)。
namespace Subtask {
struct SEG {
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
struct node {
ll f,mxf;
node(ll f=0,ll mxf=0):f(f),mxf(mxf) {}
void down(ll F) {
f+=F,mxf+=F;
}
} tr[N<<2];
SEG() {}
void Up(int p) {
tr[p].mxf=max(tr[ls].mxf,tr[rs].mxf);
}
void Down(int p) {
if(tr[p].f)tr[ls].down(tr[p].f),tr[rs].down(tr[p].f),tr[p].f=0;
}
void Build(int p=1,int l=0,int r=b[0]) {
tr[p]=node(0,0);
if(l==r)return tr[p]=!l?node(0,0):node(-LINF,-LINF),void();
Build(ls,l,mid),Build(rs,mid+1,r),Up(p);
}
void Plus(ll d) {
return tr[1].down(d);
}
void toMax(int x,ll d,int p=1,int l=0,int r=b[0]) {
if(l==r)return tomax(tr[p].f,d),tomax(tr[p].mxf,d),void();
return Down(p),x<=mid?toMax(x,d,ls,l,mid):toMax(x,d,rs,mid+1,r),Up(p);
}
ll Max() {
return tr[1].mxf;
}
ll Query(int x,int p=1,int l=0,int r=b[0]) {
if(l==r)return tr[p].f;
return Down(p),x<=mid?Query(x,ls,l,mid):Query(x,rs,mid+1,r);
}
#undef ls
#undef rs
#undef mid
} seg;
int Cmain() {
seg.Build();
FOR(i,1,n) {
ll F(max(seg.Max(),seg.Query(a[i])+b[a[i]]));
if(a[i]==a[i-1])seg.Plus(b[a[i]]);
seg.toMax(a[i-1],F);
}
wr(seg.Max());
return 0;
}
}
标记优化
发现上面没有区间操作,那么根本用不到线段树(不要忘本),直接用一个变量作为标记表示全局加,再来一个变量表示最值,进行简单的更改即可。
时间复杂度:\(O(Tn)\),空间复杂度:\(O(n+m)\)。
namespace Subtask {
ll mx,sum;
ll f[M];
int Cmain() {
mx=0,sum=0;
FOR(i,1,n)f[a[i]]=-LINF;
FOR(i,2,n) {
if(a[i]==a[i-1])sum+=a[i];
else f[a[i-1]]=max(mx,f[a[i]]+a[i]),tomax(mx,f[a[i-1]]);
}
wr(mx+sum);
return 0;
}
}
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define EDGE(g,i,x,y) for(int i=(g).h[(x)],y=(g)[(i)].v;~i;y=(g)[i=(g)[i].nxt].v)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);return Main();}signed Main
using namespace std;
constexpr int N(2e5+10),M(1e6+10);
namespace IOstream {
#define getc() getchar()
#define putc(ch) putchar(ch)
#define isdigit(ch) ('0'<=(ch)&&(ch)<='9')
template<class T>void rd(T &x) {
static bool sign(0);
static char ch(0);
for(x=0,sign=0,ch=getc(); !isdigit(ch); ch=getc())if(ch=='-')sign=1;
for(; isdigit(ch); x=(x<<1)+(x<<3)+(ch^'0'),ch=getc());
if(sign)x=-x;
}
template<class T>void wr(T x,const char End='\n') {
static int top(0);
static int st[50];
do st[++top]=x%10,x/=10;
while(x);
while(top)putc(st[top]^'0'),--top;
putc(End);
}
} using namespace IOstream;
int Cas,n;
int a[N];
namespace Subtask1 {
const int N(2e3+10);
int b[N];
ll ans;
ll f[N][N];
bool Check() {
return n<=2000;
}
int Cmain() {
sort(b+1,b+n+1),b[0]=unique(b+1,b+n+1)-b-1;
FOR(i,1,n)a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b;
RCL(f[0],-0x3f,f[0],1),f[0][0]=0,ans=0;
FOR(i,1,n) {
RCL(f[i],-0x3f,f[i],1);
FOR(j,0,b[0]) {
tomax(f[i][j],f[i-1][j]+(a[i-1]==a[i]?b[a[i]]:0));
tomax(f[i][a[i-1]],f[i-1][j]+(j==a[i]?b[a[i]]:0));
}
}
FOR(j,0,b[0])tomax(ans,f[n][j]);
wr(ans);
return 0;
}
}
namespace Subtask {
ll mx,sum;
ll f[M];
int Cmain() {
mx=0,sum=0;
FOR(i,1,n)f[a[i]]=-LINF;
FOR(i,2,n) {
if(a[i]==a[i-1])sum+=a[i];
else f[a[i-1]]=max(mx,f[a[i]]+a[i]),tomax(mx,f[a[i-1]]);
}
wr(mx+sum);
return 0;
}
}
int Cmain() {
rd(n);
FOR(i,1,n)rd(a[i]);
// if(Subtask1::Check())return Subtask1::Cmain();
return Subtask::Cmain();
}
signed main() {
for(rd(Cas); Cas; --Cas)Cmain();
return 0;
}