J. Jewel Grab
Tartarus
_Wallace_
转化询问:对于一个询问 [s,k],找到一个最长的区间 [s,t],满足区间中出现次数超过一次的元素,的出现次数减一,的和,不超过 k。
对于该区间[s,t] 区间数颜色:也就是每种颜色只算一个权值求最大和(显然每种颜色都取最大的权值)于是有下面转化:对于所有区间中出现次数超过一次的元素,找出其中权值最大的求和;对其他的元素(仅仅出现一次)直接求和。计算最终结果。
区间数颜色显然要维护一个pre[x]数组表示前一个出现c[x]的位置在哪?
首先我们考虑如何找到区间的端点t,也就是找到[s,t]这个区间。由于k非常小,考虑一次只忽略一个宝石,也就是每次找到第一个 pre[x]>s 的 x 即可,那么由于pre[x]和x都在所考虑区间内部,于是就需要多一次忽略。
显然c[x]就是出现次数超过一次的元素 于是只需要记录mc[c[x]]每次选择最大的。而对于[cur,x)这段区间都是其他的元素(仅仅出现一次)直接区间求和即可计算贡献。
考虑如何修改?
每当修改一个位置的颜色最多会有3个位置的pre[]发生改变:
- 该位置pre一定改变
- 原颜色后面位置的pre(可能)改变
- 新颜色后面位置的pre(可能)改变
可以考虑用一个双链表pre[],nxt[]维护一下。详细参考代码,代码都是抄第一篇博客的。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{T res=0;T fg=1;char ch=getchar();while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res*fg;
}
const int N=200010;int n,m;
int pre[N],last[N],nxt[N];
int c[N];ll v[N],mc[N];
set<int> cp[N];
void prework()
{memset(last,-1,sizeof last);for(int i=n;i>=1;i--){nxt[i]=last[c[i]];last[c[i]]=i;}memset(last,-1,sizeof last);for(int i=1;i<=n;i++) {pre[i]=last[c[i]];last[c[i]]=i;cp[c[i]].insert(i);}
}
struct node
{int l,r;int v;ll s;
}t[N<<2];
void pushup(int u)
{t[u].v=max(t[u<<1].v,t[u<<1|1].v);t[u].s=t[u<<1].s+t[u<<1|1].s;
}
void build(int u,int l,int r)
{t[u]={l,r};if(l==r){t[u].v=pre[l];t[u].s=v[l];return;}int mid=l+r>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);pushup(u);
}
void modify(int u,int p)
{if(t[u].l==t[u].r) {t[u].v=pre[p];t[u].s=v[p];return;}int mid=t[u].l+t[u].r>>1;if(p<=mid) modify(u<<1,p);else modify(u<<1|1,p);pushup(u);
}
ll query(int u,int l,int r)
{if(l<=t[u].l&&t[u].r<=r) return t[u].s;int mid=t[u].l+t[u].r>>1;ll v=0;if(l<=mid) v+=query(u<<1,l,r);if(r>mid) v+=query(u<<1|1,l,r);return v;
}
int find(int u,int s,int p)
{if(t[u].l==t[u].r) return t[u].l;int mid=t[u].l+t[u].r>>1;int pos=-1;if(t[u<<1].v>=s&&p<=mid) pos=find(u<<1,s,p);if(pos!=-1) return pos;if(t[u<<1|1].v>=s)pos=find(u<<1|1,s,p);return pos;
}
int main()
{n=rd(),m=rd();for(int i=1;i<=n;i++) c[i]=rd(),v[i]=rd();prework();build(1,1,n);while(m--){int op=rd();if(op==1){int x=rd(),c0=rd(),v0=rd();cp[c[x]].erase(x);// 最后一次出现的c[x]的位置if(last[c[x]]==x) last[c[x]]=pre[x];v[x]=v0;c[x]=c0;// 删除的修改// 链表的一些删除操作if(nxt[x]!=-1) {if(pre[x]!=-1) {nxt[pre[x]]=nxt[x];pre[nxt[x]]=pre[x];modify(1,nxt[x]);// nxt[x]的pre[]发生改变需要维护}else{pre[nxt[x]]=-1;modify(1,nxt[x]);}}else{if(pre[x]!=-1) nxt[pre[x]]=-1;}// 插入的修改int pos;set<int>::iterator it;it=cp[c0].lower_bound(x);// 插入在末尾if(it==cp[c0].end()){nxt[x]=-1;pre[x]=last[c0];if(pre[x]!=-1) nxt[pre[x]]=x;modify(1,x);}else{pos=*it;if(pre[pos]!=-1) //插入不在开头{nxt[pre[pos]]=x;pre[x]=pre[pos];modify(1,x);}// 插入在开头else{pre[x]=-1;modify(1,x);}nxt[x]=pos;pre[pos]=x;modify(1,pos);}cp[c0].insert(x);last[c0]=max(last[c0],x);}else{int s=rd(),k=rd();vector<int> col;int cur=s;ll ans=0;for(int j=0;j<=k&&cur<=n;j++){int pos=find(1,s,cur);if(pos==-1) {ans+=query(1,cur,n);break;}col.push_back(c[pos]);if(j<k){// 出现一次直接选if(mc[c[pos]]==0) mc[c[pos]]=v[pre[pos]];// 出现第二次考虑与第一次出现的比较if(v[pos]>mc[c[pos]]){ans-=mc[c[pos]];ans+=v[pos];mc[c[pos]]=v[pos];}}ans+=query(1,cur,pos-1);// [cur,pos-1]都是第一次出现cur=pos+1;}printf("%lld\n",ans);for(int u:col) mc[u]=0;}}return 0;
}
太难了吧。。
要加油哦~