[Ynoi2016] 镜中的昆虫
[Ynoi2016] 镜中的昆虫
P4690 [Ynoi2016] 镜中的昆虫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
您正在欣赏 galgame 的 HS,然后游戏崩溃了,于是您只能做数据结构题了:
维护一个长为 \(n\) 的序列 \(a_i\),有 \(m\) 次操作。
-
将区间 \([l,r]\) 的值修改为 \(x\)。
-
询问区间 \([l,r]\) 出现了多少种不同的数,也就是说同一个数出现多次只算一个。
输入格式
第一行两个整数 \(n,m\)。
第二行 \(n\) 个整数表示 \(a_i\)。
后面 \(m\) 行每行为 \(1\ l\ r\ x\) 或者 \(2\ l\ r\) ,分别表示修改和询问。
输出格式
对于每个询问,输出一个数表示答案。
SOLUTION
题解 P4690 【[Ynoi2016]镜中的昆虫】 - shadowice1984 的博客 - 洛谷博客 (luogu.com.cn)
(要是没有get what meaning it is建议配合代码和注释食用)
CODE
struct qes{int op,l,r,x;}q[N];
struct qry{int t,l,r,ans;}Q[N];
inline bool operator<(qry x,qry y){
return x.l<y.l;
}
inline bool cmp(qry x,qry y){
return x.t<y.t;
}
inline bool cp1(int x,int y){
return pre[x]<pre[y];
}
struct poss{int t,pos,pre,val;}P[10*N];
inline bool operator<(poss x,poss y){
return x.pre<y.pre;
}
inline void modify(int i,int p){
if(npre[i]==p)return ;
P[++tp1]={++cnt,i,npre[i],-1};
P[++tp1]={++cnt,i,npre[i]=p,1};
}
namespace Set{
struct data{int l,r,x;};
inline bool operator<(data a,data b){
return a.r<b.r;
}
set<data>s;
struct node{int l,r;};
inline bool operator<(node x,node y){
return x.r<y.r;
}
set<node>c[2*N];
set<int>cg;
inline void splt(int mid){//将一个节点拆成两个节点
auto p=*(s.lower_bound((data){0,mid,0}));
if(mid==p.r)return ;
s.erase(p);
s.insert({p.l,mid,p.x});
s.insert({mid+1,p.r,p.x});
c[p.x].erase({p.l,p.r});
c[p.x].insert({p.l,mid});
c[p.x].insert({mid+1,p.r});
}
inline void ins(data p){
s.insert(p);
auto it=c[p.x].insert({p.l,p.r}).fir;
it++;
if(it!=c[p.x].end())cg.insert(it->l);
}
inline void del(set<data>::iterator it){
cg.insert(it->l);
auto it1=c[it->x].find((node{it->l,it->r}));
auto it2=it1;
it2++;
if(it2!=c[it->x].end()){
cg.insert(it2->l);
}
c[it->x].erase(it1);
s.erase(it);
}
inline void stv(int l,int r,int x){//区间赋值
if(l!=1)splt(l-1);
splt(r);//处理可能会是交集的区间 (分成变/没变两部分)
int p=l;
while(p!=r+1){//删除包含的区间
auto it=s.lower_bound((data){0,p,0});
p=it->r+1;
del(it);
}
ins({l,r,x});
//扫一遍set处理所有变化的pre值
for(auto it=cg.begin();it!=cg.end();it++){
auto it1=s.lower_bound({0,*it,0});
if(*it!=it1->l) modify(*it,*it-1);//不是左端点
else{//左端点
auto it2=c[it1->x].lower_bound({0,*it});
if(it2!=c[it1->x].begin()){
it2--;
modify(*it,it2->r);//it2.r是pre前缀
}else{//无前缀
modify(*it,0);
}
}
}
cg.clear();
}
inline void insert(){//set插入连续一段
int now=a[1],ct=1;
for(int i=2;i<=n;i++){
if(now!=a[i]){
s.insert({i-ct,i-1,now});//st ed val
c[now].insert({i-ct,i-1});
now=a[i];
ct=1;
}else ct++;
}
s.insert((data){n-ct+1,n,a[n]});
c[a[n]].insert({n-ct+1,n});
}
}
struct TA{//tree-arrey
int c[N];
inline int lowbit(int x){
return x&(-x);
}
inline void C(int x,int t){
while(x<=n){
c[x]+=t;
x+=lowbit(x);
}
}
inline void D(int x){//delete
while(x<=n){
c[x]=0;
x+=lowbit(x);
}
}
inline int Q(int x){//query
int res=0;
while(x){
res+=c[x];
x-=lowbit(x);
}
return res;
}
inline void clear(){
for(int i=1;i<=n;i++){
c[i]=0;
}
}
}ta;
inline void CDQ(int l1,int r1,int l2,int r2,int L,int R){
if(l1==r1||l2==r2)return ;
int mid=(L+R)>>1;
int mid1=l1;while(mid1!=r1&&P[mid1+1].t<=mid)mid1++;
int mid2=l2;while(mid2!=r2&&Q[mid2+1].t<=mid)mid2++;
CDQ(l1,mid1,l2,mid2,L,mid);CDQ(mid1,r1,mid2,r2,mid,R);
///做了(l,mid)的修改&&(mid+1,r)的询问
if(l1!=mid1&&mid2!=r2){
sort(P+l1+1,P+mid1+1);//按pre排序
sort(Q+mid2+1,Q+r2+1);//按l排序
for(int i=mid2+1,j=l1+1;i<=r2;i++){
while(j<=mid1&&P[j].pre<Q[i].l)ta.C(P[j].pos,P[j].val),j++;//pre<l的
//由于Q按照l从小到大,所以操作之后(将pre<l的操作更新)
Q[i].ans+=ta.Q(Q[i].r)-ta.Q(Q[i].l-1);//(l,r)内pre<l的个数
}
for(int i=l1+1;i<=mid1;i++){
ta.D(P[i].pos);//还原 (清0)
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
mp[a[i]]=1;
}
for(int i=1;i<=m;i++){
int op,l,r,x=0;
scanf("%d%d%d",&op,&l,&r);
if(op==1){
scanf("%d",&x);
mp[x]=1;
}
q[i]={op,l,r,x};
}
auto it=mp.begin(),itt=it;++itt;
while(itt!=mp.end()){
itt->sec+=it->sec;
it++,itt++;
}
for(int i=1;i<=n;i++){
a[i]=mp[a[i]];
}
for(int i=1;i<=m;i++){
if(q[i].op==1)q[i].x=mp[q[i].x];
}
for(int i=1;i<=n;i++){
npre[i]=pre[i]=lst[a[i]];
lst[a[i]]=i;
}
Set::insert();
for(int i=1;i<=m;i++){
if(q[i].op==1){
Set::stv(q[i].l,q[i].r,q[i].x);
}else{
Q[++tp2]={++cnt,q[i].l,q[i].r,0};
}
}
sort(Q+1,Q+1+tp2);
for(int i=1;i<=n;i++){
srt[i]=i;
}
sort(srt+1,srt+1+n,cp1);
//初始计算
for(int i=1,j=1;i<=tp2;i++){
while(j<=n&&pre[srt[j]]<Q[i].l) ta.C(srt[j],1),j++;
Q[i].ans+=ta.Q(Q[i].r)-ta.Q(Q[i].l-1);
}
ta.clear();
sort(Q+1,Q+tp2+1,cmp);
CDQ(0,tp1,0,tp2,0,cnt);
sort(Q+1,Q+tp2+1,cmp);
for(int i=1;i<=tp2;i++){
printf("%d\n",Q[i].ans);
}
return 0;
}
(先占个坑🕳)