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