[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;
}

posted @ 2024-11-13 14:07  plus_cat  阅读(28)  评论(0编辑  收藏  举报