.article-info-tag,button{text-transform:uppercase}.day,.postMeta,.postSticky{position:relative}.postTitle a:link,html{-webkit-tap-highlight-color:transparent}#blog-calendar,.code-copay-btn,.code-hljs-len,.hidden{visibility:hidden}#EntryTag,#blogTitle h1{margin-top:20px}#EntryTag a,.postSticky{background:#6fa3ef}#blogTitle h1 a:hover,.dayTitle a,a,a:active,a:link,a:visited{color:#5c8ec6}#calendar table a:hover,#navList a:hover,.postDesc a:hover,a:active,a:hover,a:link,a:visited,button{text-decora…ryTag a:visited{color:#666}#BlogPostCategory a,#EntryTag a{height:20px;line-height:20px;color:#fff!important;padding:3px 5px;border-radius:3px;margin:2px 5px 0;text-decoration:none;font-size:14px}#BlogPostCategory a:hover,#EntryTag a:hover{transition:all .3s linear 0s;opacity:.8}#topics .postDesc{padding-left:0;width:100%;text-align:left;color:#666;margin-top:5px;background:0 0}.feedbackListSubtitle-louzhu:after,.feedbackListSubtitle:after,.feedbackListSubtitle:before{top:11px;right:100%;left:-1

P4135 作诗 题解

Description

给定长度为 \(n\) 的序列 \(a\),每个点有一种颜色 \(c\)
\(m\) 次询问区间 \([l,r]\),求有多少个颜色出现次数为偶数次。

限制\(1\le n,m,c\le10^5\)

Solution

考虑 分块
用类似于求区间众数的思路:
\(ans_{i,j}\) 来存从第 \(i\) 到第 \(j\) 块的答案(即有多少数出现偶数次)。
\(cnt_{i,j}\) 来存前 \(i\) 块中 \(j\) 的出现次数。

预处理可以开桶算,对于询问设初始答案为 \(ans_{posl+1,posr-1}\),零散块暴力统计,记得每次要清空桶。

设块长为 \(S\),则预处理 \(cnt\) 的复杂度为 \(\Theta(Sc)\),预处理 \(ans\) 的复杂度为 \(\Theta(nS)\),单次询问为 \(\Theta(n/S+S)\)

\(S=\sqrt n\) 时最优为 \(\Theta(n\sqrt n)\)

细节见代码:

Code

constexpr int N = 1e5 + 10 ;
constexpr int M =  1100 ;

int n,m,c,siz,tot ;
int L[N],R[N],a[N],cnt[M][N],ans[M][M];
int t[N] ;

inline int pos(const int x) {
    return (x-1)/siz + 1;
}

inline void Init() {
    cin >> n >> c >> m,siz = sqrt(n),tot = pos(n) ;
    for(int i = 1;i <= n;++i) cin >> a[i],++cnt[pos(i)][a[i]];
    for(int i = 1;i <= tot;++i) L[i] = R[i-1] + 1,R[i] = i*siz ;
    R[tot] = n ;
    for(int i = 1;i <= tot;++i) 
        for(int j = 0;j <= c;++j) 
            cnt[i][j] += cnt[i-1][j] ;
    for(int i = 1;i <= tot;++i) {
        for(int j = i;j <= tot;++j) {
            ans[i][j] = ans[i][j-1] ;
            for(int k = L[j];k <= R[j];++k) {
                ++t[a[k]] ;
                if(t[a[k]] % 2 == 0) ++ans[i][j] ;
                else if(t[a[k]] >= 3) -- ans[i][j] ;
            }
        }
        memset(t,0,sizeof t) ;
    }
    
}

inline int calc(const int l,const int r) {
    int res = 0;
    for(int i = l;i <= r;++i) {
        ++t[a[i]] ;
        if(t[a[i]] % 2 == 0) ++res ;
        else if(t[a[i]] >= 3) --res ;
    }
    for(int i = l;i <= r;++i) --t[a[i]] ;
    return res ;
}

inline int query(const int l,const int r) {
    const int posl = pos(l),posr = pos(r) ;
    if(posl + 1 >= posr) return calc(l,r) ;
    int res = ans[posl+1][posr-1] ;
    for(int i = l;i <= R[posl];++i) {
        const int tmp = cnt[posr-1][a[i]] - cnt[posl][a[i]] ;
        t[a[i]]++ ;
        if((t[a[i]]+tmp) % 2 == 0) ++res ;
        else if(t[a[i]]+tmp >= 3) --res ;
    }
    for(int i = L[posr];i <= r;++i) {
        t[a[i]]++;
        const int tmp = cnt[posr-1][a[i]] - cnt[posl][a[i]] ;
        if((t[a[i]]+tmp) % 2 == 0) ++res ;
        else if(t[a[i]]+tmp >= 3) --res ;
    }
    for(int i = l;i <= R[posl];++i) --t[a[i]] ;
    for(int i = L[posr];i <= r;++i) --t[a[i]] ;
    return res ; 
}

int main(int argc,const char **argv) {
    Init() ;
    int lastans = 0 ;
    while(m--) {
        int l,r;
        cin >> l >> r;
        l = (l + lastans) % n + 1,r = (r + lastans) % n + 1;
        if(l > r) std::swap(l,r);
        l = max(l,1),r = min(r,n) ;
        cout << (lastans = query(l,r)) << '\n' ;
    }    
    return 0 ;
}
posted @ 2021-04-13 14:37  feicheng  阅读(33)  评论(0编辑  收藏  举报