Loading

P3582 [POI2015] KIN

题意

给一段带颜色的序列,每种颜色有特定的权值。求一个 \(l,r\),使得只出现一次的颜色的权值和最大。

Solution

\(\texttt{E}\color{red}{\texttt{ricQian}}\) 教我!

这题的套路好像挺经典的样子。假如说我们把左端点当成行,右端点当成列,那么我们考虑一个区间可以转化成矩形里的一个点。那么考虑每一种颜色的贡献,相当于在矩形内做矩形加,全局求最大值。

也许会去码树套树。但鉴于这题允许离线,我们有更高明的做法。

考虑一个差分,比如我现在需要在 \(l_1,r_1,l_2,r_2\) 这个矩形内加 \(w\)。那我们可以在第 \(l_1\) 行的 \([l_2,r_2]\)\(w\),在 \(r_1+1\) 行的 \([l_2,r_2]\) 减一,然后最终前缀和一下就可以了。

考虑用线段树优化这个东西。你可以把所有的修改操作离线下来,然后对于每一行先进行当前行的修改,然后取线段树上最大值。可以理解成同时实现差分然后前缀和。

但是这题是卡空间的捏~注意你的内存。。。

Code

using namespace std;
const int MAXN=1e6+10;
struct Tree{ll inc,mx;}tr[MAXN*3];
#define ls i<<1
#define rs i<<1|1
void build(int i,int l,int r){
	tr[i].inc=tr[i].mx=0;
	if(l==r) return;int mid=(l+r)>>1;
	build(ls,l,mid);build(rs,mid+1,r);
}
void pushup(int i){tr[i].mx=max(tr[ls].mx,tr[rs].mx);}
void add(int i,ll v){tr[i].mx+=v;tr[i].inc+=v;}
void pushdown(int i){
	if(!tr[i].inc) return;
	add(ls,tr[i].inc);add(rs,tr[i].inc);
	tr[i].inc=0;
}
void upd(int i,int L,int R,int l,int r,int v){
	if(L==l&&R==r) return add(i,v);
	pushdown(i);int mid=(L+R)>>1;
	if(r<=mid) upd(ls,L,mid,l,r,v);
	else if(l>mid) upd(rs,mid+1,R,l,r,v);
	else upd(ls,L,mid,l,mid,v),upd(rs,mid+1,R,mid+1,r,v);
	pushup(i);
}
int w[MAXN];
vector<int> p[MAXN];
struct Updates{int l,r,v;};
vector<Updates> q[MAXN];
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n,m,f;cin>>n>>m;
	rep(i,1,m) p[i].pb(0);
	rep(i,1,n) cin>>f,p[f].pb(i);
	rep(i,1,m) p[i].pb(n+1);
	rep(i,1,m) cin>>w[i];
	build(1,1,n);
	rep(i,1,m) rep(j,1,(int)p[i].size()-2){
		q[p[i][j-1]+1].pb(Updates{p[i][j],p[i][j+1]-1,w[i]});
		q[p[i][j]+1].pb(Updates{p[i][j],p[i][j+1]-1,-w[i]});
	}ll ans=0;
	rep(i,1,n){
		for(auto u:q[i])
			upd(1,1,n,u.l,u.r,u.v);
		ans=max(ans,tr[1].mx);
	}
	cout<<ans<<'\n';
	return 0;
}
posted @ 2022-07-21 14:41  ZCETHAN  阅读(135)  评论(2编辑  收藏  举报