1. 程式人生 > >BZOJ 2821: 作詩(Poetize)(分塊)

BZOJ 2821: 作詩(Poetize)(分塊)

題面

Time Limit: 50 Sec Memory Limit: 128 MB
Submit: 3596 Solved: 1070
[Submit][Status][Discuss]
Description
神犇SJY虐完HEOI之後給傻×LYD出了一題:SHY是T國的公主,平時的一大愛好是作詩。由於時間緊迫,SHY作完詩
之後還要虐OI,於是SHY找來一篇長度為N的文章,閱讀M次,每次只閱讀其中連續的一段[l,r],從這一段中選出一
些漢字構成詩。因為SHY喜歡對偶,所以SHY規定最後選出的每個漢字都必須在[l,r]裡出現了正偶數次。而且SHY認
為選出的漢字的種類數(兩個一樣的漢字稱為同一種)越多越好(為了拿到更多的素材!)。於是SHY請LYD安排選
法。LYD這種傻×當然不會了,於是向你請教……問題簡述:N個數,M組詢問,每次問[l,r]中有多少個數出現正偶
數次。
Input
輸入第一行三個整數n、c以及m。表示文章字數、漢字的種類數、要選擇M次。第二行有n個整數,每個數Ai在[1, c]間,代表一個編碼為Ai的漢字。接下來m行每行兩個整數l和r,設上一個詢問的答案為ans(第一個詢問時ans=0),
令L=(l+ans)mod n+1, R=(r+ans)mod n+1,若L>R,交換L和R,則本次詢問為[L,R]。
Output
輸出共m行,每行一個整數,第i個數表示SHY第i次能選出的漢字的最多種類數。

Sample Input
5 3 5

1 2 2 3 1

0 4

1 2

2 2

2 3

3 5
Sample Output
2

0

0

0

1
HINT
對於100%的資料,1<=n,c,m<=10^5

解題思路

  區間出現偶數次的個數,強制線上無法莫隊。那隻好分塊了,剛開始寫了個\(n^{5/3}\)的醜陋分塊,直接\(T\)飛。後來%了%題解發現自己預處理寫醜了,其實可以\(nsqrt(n)\),預處理倒序列舉塊,然後在從塊的右端列舉點。算出\(cnt[i][j]\)表示\(i\)這個數字在前\(j\)塊的出現次數,\(ans[i][j]\)表示\(i,j\)兩塊的答案。詢問時整塊直接呼叫\(ans\)

,邊角塊暴力列舉修改答案,時間複雜度O(nsqrt(n))

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
 
using namespace std;
const int MAXN = 100005;
 
inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return f?x:-x;
}
 
int n,c,m,a[MAXN],siz,cnt[MAXN][320],num,Ans;
int bl[MAXN],l[MAXN],r[MAXN],ans[320][320],tmp[MAXN]; 
 
int query(int x,int y){
    if(bl[x]==bl[y]){
        int ret=0;
        for(int i=x;i<=y;i++){
            tmp[a[i]]++;
            if((tmp[a[i]]&1) && (tmp[a[i]]!=1)) ret--; 
            else if(!(tmp[a[i]]&1)) ret++;
        }
        for(int i=x;i<=y;i++) tmp[a[i]]=0;
        return ret;
    }
    int ret=ans[bl[x]+1][bl[y]-1],now;
    for(int i=x;i<=r[bl[x]];i++) {
        tmp[a[i]]++;now=cnt[a[i]][bl[y]-1]-cnt[a[i]][bl[x]]+tmp[a[i]];
        if(now>1 && (now&1)) ret--;
        else if(!(now&1)) ret++;
    }
    for(int i=l[bl[y]];i<=y;i++){
        tmp[a[i]]++;now=cnt[a[i]][bl[y]-1]-cnt[a[i]][bl[x]]+tmp[a[i]];
        if(now>1 && (now&1)) ret--;
        else if(!(now&1)) ret++;
    }
    for(int i=x;i<=r[bl[x]];i++) tmp[a[i]]--;
    for(int i=l[bl[y]];i<=y;i++) tmp[a[i]]--; 
    return ret;
}
 
int main(){
    n=rd(),c=rd(),m=rd();
    siz=sqrt(n)+1;num=n/siz;if(n%siz) num++;
    for(int i=1;i<=n;i++) 
        a[i]=rd(),bl[i]=(i-1)/siz+1;
    for(int i=1;i<=num;i++)
        l[i]=(i-1)*siz+1,r[i]=i*siz;
    r[num]=n;
    for(int i=num;i;i--){
        int now=0;
        for(int j=r[i];j;j--){
            cnt[a[j]][i]++;
            if((cnt[a[j]][i]&1) && cnt[a[j]][i]!=1) now--;
            else if(!(cnt[a[j]][i]&1)) now++;
            if(bl[j]!=bl[j-1]) ans[bl[j]][i]=now; 
        }   
    }
//  for(int i=1;i<=num;i++)
//      for(int j=1;j<=c;j++)
//          cnt[j][i]+=cnt[j][i-1];
//    for(int i=1;i<=num;i++)
//        for(int j=l[i];j<=r[i];j++)
//            cnt[a[j]][i]++;
//    for(int i=1;i<=num;i++) 
//        for(int j=1;j<=c;j++)
//            cnt[j][i]+=cnt[j][i-1]; 
//    for(int i=1;i<=num;i++)
//        for(int j=i;j<=num;j++)
//            for(int k=1;k<=c;k++)
//                if(cnt[k][j]-cnt[k][i-1]>0 && (!((cnt[k][j]-cnt[k][i-1])&1)))
//                    ans[i][j]++;
    int L,R;
    while(m--){
        L=rd(),R=rd();
        L=(Ans+L)%n+1,R=(Ans+R)%n+1;
        if(L>R) swap(L,R);
        Ans=query(L,R);
        printf("%d\n",Ans);
    } 
    return 0;
}