luogu P4690 [Ynoi2016] 镜中的昆虫 题解
P4690 [Ynoi2016] 镜中的昆虫 题解
题意
维护一个长为 \(n\) 的序列 \(a_i\),有 \(m\) 次操作。
-
将区间 \([l,r]\) 的值修改为 \(x\)。
-
询问区间 \([l,r]\) 出现了多少种不同的数,也就是说同一个数出现多次只算一个。
题解
感觉这道题还是比较有意思的,像是一堆套路被集合了起来,但难写是真的难写,细节是真的多。
区间数颜色
以前我写区间数颜色是只算最右边的,然后修改前驱,但是这样写并不好扩展。
我们只计算最左边的,只我们记\(i\)的前驱为\(pre_i\),前驱是前面第一个和自己相同的数字。
然后扫描线统计区间中\([l,r]\)中\(pre_i\)为\([0,l-1]\)的个数,这其实是一个矩阵查询。
这其实是一个经典的二维数点问题,将\((i,pre_i)\)看做一个点,相当于查询\((x,y)(l\leq x \leq r)(0 \leq y \leq l-1)\)的点个数。
运用二维差分的知识将一个询问拆成\(4\)个,然后跑二维偏序即可,可能画图会直观一些,但是懒。
单点带修数颜色
也就是[BalkanOI2007] Mokia 摩基亚这道题。
多引入一维时间\(t\),给每一个点设置加入时间,询问设置询问时间,一个点会被统计当且仅当\(x,y,t\)都小于时才会被统计。
修改一个点可以改为插入一个点和删除一个点,修改一个点还要修改后继的前驱等,比较麻烦。
区间带修数颜色
其实对于上面的操作并不是进阶很多,我们将由于有区间推平操作,想到珂朵莉树,一个区间看做一个一个颜色段,每次修改发现只会对颜色端的两端有影响,只有颜色段两端的\(pre_i\)会被修改。
而我们知道只有修改的珂朵莉树操作次数是 n+m 级别的,所以实际上修改的并不是很多,给每一个颜色开一个珂朵莉树,给原序列开一颗珂朵莉树进行维护就行了。
一些卡常小建议:
- 三维偏序中结构体只需要存4个,多的放外面,可以加快排序速度。
- 比较短的,可以展开的函数加上
inline
。 - 在三维偏序中最后一层不需要排序。
- 尽量减少无用的点询问和插入
bool
可以转bitset
code
#include <bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
#define x first
#define y second
#define rg register
#define pc putchar
#define gc getchar
#define pf printf
#define space pc(' ')
#define enter pc('\n')
#define me(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define FOR(i,k,t,p) for(rg int i(k) ; i <= t ; i += p)
#define ROF(i,k,t,p) for(rg int i(k) ; i >= t ; i -= p)
using namespace std ;
bool s_gnd ;
inline void read(){}
template<typename T,typename ...T_>
inline void read(T &x,T_&...p)
{
x = 0 ;rg int f(0) ; rg char c(gc()) ;
while(!isdigit(c)) c = gc() ;
while(isdigit(c)) x = (x<<1)+(x<<3)+(c^48),c = gc() ;
read(p...);
}
int stk[10],tpp ;
inline void print(){}
template<typename T,typename ...T_>
inline void print(T x,T_...p)
{
do stk[++tpp] = x%10,x /= 10 ; while(x) ;
while(tpp) pc(stk[tpp--]^48) ; print(p...) ;
}
bool S_GND ;
const int N = 2e5+5 ;
const int M = 1e6+5+N ;
pii b[N] ;
int n,q,top,cnt,ed,ti(1) ;
struct RP{int op,l,r ;}qu[N] ;
int a[N],pre[N],X[N],tr[N],las[N],ans[N],id[M] ;
bitset<M>tp ; short op[M] ;
struct Pia{int x,y,z,of ;}opr[M] ;
int cmp1(Pia x,Pia y)
{
return x.x!=y.x?x.x<y.x:(x.y!=y.y?x.y<y.y:x.z<y.z) ;
}
int cmp2(Pia x,Pia y)
{
return x.y!=y.y?x.y<y.y:x.z<y.z ;
}
inline void add(int x,int k)
{
while(x <= q+2) tr[x] += k,x += -x&x ;
}
inline int ask(int x)
{
int res = 0 ; while(x)
res += tr[x],x -= -x&x ;
return res ;
}
// vector<Pia>ass ;
void Solve(int le,int ri)
{
if(le == ri) return ;
int mid = le+ri>>1,po = le,sum = 0 ;
Solve(le,mid),Solve(mid+1,ri) ;
FOR(i,mid+1,ri,1)
{
while(opr[po].y <= opr[i].y && po <= mid)
{
if(!tp[opr[po].of])
{
int rp = op[opr[po].of] ;
if(opr[po].z) add(opr[po].z,rp) ;
else sum += rp ;
}
++po ;
}
if(tp[opr[i].of]) ans[id[opr[i].of]] += ask(opr[i].z)*op[opr[i].of]+sum ;
}
FOR(i,le,po-1,1) if(!tp[opr[i].of] && opr[i].z) add(opr[i].z,-op[opr[i].of]) ;
if(!(le == 1 && ri == cnt)) sort(opr+le,opr+ri+1,cmp2) ;
}
struct Node
{
int l,r,val ;
Node(int L):l(L){} ;
Node(int L,int R):l(L),r(R){} ;
Node(int L,int R,int Val):l(L),r(R),val(Val){} ;
bool operator < (const Node &A) const{return l < A.l ;}
} ;
set<Node>s[N],sq ;
#define sit set<Node>::iterator
map<int,int>mip ;
inline void Add(int x,int y,int k,int ti)
{
opr[++cnt] = {x,y,ti,cnt},op[cnt] = k ;
}
sit Split(set<Node>&t,int pos)
{
auto it = t.lower_bound(Node(pos)) ; if(it->l == pos) return it ; it-- ;
auto [l,r,val] = *it ; t.erase(it),t.insert(Node(l,pos-1,val)) ;
if(pos <= r) return t.insert(Node(pos,r,val)).first ; return t.lower_bound(pos+1) ;
}
void Delete(int x,int l,int r,int ti)
{
auto it2 = Split(s[x],r+1),it3 = Split(s[x],l),it1 = it3 ; it3-- ;
if(it2->l != n+1) Add(it2->l,las[l],1,ti),Add(it2->l,r,-1,ti),las[it2->l] = las[l] ;
Add(l,las[l],-1,ti),s[x].erase(it1,it2) ;
}
void Insert(int x,int l,int r,int ti)
{
s[x].insert(Node(l,r)) ; auto it2 = Split(s[x],r+1),it3 = Split(s[x],l),it1 = it3 ; it3-- ;
if(it2->l != n+1) Add(it2->l,it3->r,-1,ti),Add(it2->l,it1->r,1,ti),las[it2->l] = it1->r ;
Add(it1->l,it3->r,1,ti),las[it1->l] = it3->r ;
}
void Modify(int le,int ri,int k,int t)
{
auto it2 = Split(sq,ri+1),it1 = Split(sq,le) ;//,it3 = it1,it3 = it2 ;
for(auto it = it1 ; it != it2 ; ++it) if(k != it->val) Delete(it->val,it->l,it->r,t+1),Insert(k,it->l,it->r,t+1) ;
sq.erase(it1,it2),sq.insert(Node(le,ri,k)) ;
}
void Something(int t)
{
if(t > ed) return ;
if(qu[t].op == 2)
{
int x = qu[t].l,y = 0 ;
int u = qu[t].r,v = qu[t].l-1 ;
opr[++cnt] = {u,v,t+1,cnt},id[cnt] = ++top,tp[cnt] = 1,op[cnt] = 1 ;
opr[++cnt] = {x-1,v,t+1,cnt},id[cnt] = top,tp[cnt] = 1,op[cnt] = -1 ;
return ;
}
Modify(qu[t].l,qu[t].r,X[t],t) ;
}
void Debug()
{
puts("There is color") ;
FOR(i,1,n,1) if(s[i].size() > 2)
{
print(i),pf(":"),enter ;
for(auto [l,r,val]:s[i]) print(l),space,print(r),enter ;
puts("----------------------") ;
}
for(auto [l,r,val]:sq) print(l),space,print(r),space,print(val),enter ;
for(auto [x,y,val]:sq) FOR(i,1,y-x+1,1) print(val),space ; enter ;
}
signed main()
{
// cerr<<(double)(&s_gnd-&S_GND)/1024.0/1024.0 ;
// freopen("k.in","r",stdin) ;
// freopen("k.out","w",stdout) ;
// cerr<<(sizeof(ass)+sizeof(opr))/1024/1024 ;
read(n),read(q) ;
FOR(i,1,n,1) read(a[i]),b[++cnt] = mp(a[i],i) ;
FOR(i,1,q,1)
{
read(qu[i].op) ;
if(qu[i].op == 2) read(qu[i].l),read(qu[i].r),ed = i ;
else read(qu[i].l),read(qu[i].r),read(X[i]),b[++cnt] = mp(X[i],n+i) ;
}
sort(b+1,b+1+cnt),b[0].x = -1 ;//,sq.insert(Node(n+1,n+1,0)) ;
FOR(i,1,cnt,1)
{
top += b[i].x!=b[i-1].x ;
if(b[i].y <= n) a[b[i].y] = top ;
else X[b[i].y-n] = top ;
}
FOR(i,1,top,1) s[i].insert(Node(0,0,0)),s[i].insert(Node(n+1,n+1,0)) ;
cnt = 0,top = 0 ;
FOR(i,1,n,1)
{
opr[++cnt] = {i,pre[a[i]],1,cnt},op[cnt] = 1,las[i] = pre[a[i]],pre[a[i]] = i ;
s[a[i]].insert(Node(i,i,0)) ;
}
int l = 1 ;
FOR(i,2,n+1,1) if(a[i] != a[i-1])
sq.insert(Node(l,i-1,a[i-1])),l = i ;
FOR(i,1,q,1) Something(i) ;
sort(opr+1,opr+1+cnt,cmp1),Solve(1,cnt) ;
FOR(i,1,top,1) print(ans[i]),putchar('\n') ;
return 0 ;
}