luogu P4690 [Ynoi2016] 镜中的昆虫 题解

P4690 [Ynoi2016] 镜中的昆虫 题解

题意

维护一个长为 \(n\) 的序列 \(a_i\),有 \(m\) 次操作。

  1. 将区间 \([l,r]\) 的值修改为 \(x\)

  2. 询问区间 \([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 级别的,所以实际上修改的并不是很多,给每一个颜色开一个珂朵莉树,给原序列开一颗珂朵莉树进行维护就行了。

一些卡常小建议:

  1. 三维偏序中结构体只需要存4个,多的放外面,可以加快排序速度。
  2. 比较短的,可以展开的函数加上inline
  3. 在三维偏序中最后一层不需要排序。
  4. 尽量减少无用的点询问和插入
  5. 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 ;
}
posted @ 2023-05-15 10:44  谭皓猿  阅读(70)  评论(0编辑  收藏  举报