BZOJ2821(作詩)

2821: 作詩(Poetize)

Time Limit: 50 Sec  Memory Limit: 128 MB
Submit: 1004  Solved: 321
[Submit][Status]

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

【題解】分塊解決,也看了其他的題解,我的無需二分,且理論複雜度在O(n*sqrt(n)),但有一定的常數。

步驟:

   一、預處理出第 i 塊到第 j 塊的正偶數答案。

        二、預處理出前 i 塊數字 p 出現的個數。

三、處理是先通過預處理,O(1)計算l,r之間整塊的答案數量。然後在判斷兩邊零碎的數,具體判斷詳見代碼。


#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>

#define maxn 101000

using namespace std;

int f[maxn][318],w[318][318],twice[maxn],S,a[maxn],cc[maxn];

inline void read(int &x)
{
	x=0;char ch;
	while(ch=getchar(),ch==' ' || ch=='\r' || ch=='\n');
	while(ch!=' ' && ch!='\r' && ch!='\n')x=x*10+(int)ch-48,ch=getchar();	
}

int main()
{
	memset(f,0,sizeof(f));
	memset(w,0,sizeof(w));
	memset(twice,0,sizeof(twice));
	
	int n,c,m,ans=0,l;
	read(n);read(c);read(m);
	S=sqrt(n)+1;l=1;

	for(int i=1;i<=n;i++)
	{		
		read(a[i]);
		if(((int)(i/S)+1)==l)f[a[i]][l]++;
		else{
		     	l++;for(int j=1;j<=c;j++)f[j][l]+=f[j][l-1];
				f[a[i]][l]++;
		 }
	}
	
	for(int i=1;i<=l;i++)
	{
		memset(cc,0,sizeof(cc));int now=0;
	 	for(int j=i;j<=l;j++)
	 	{
	 	 	for(int p=(j-1)*S;p<=j*S-1;p++)
	   			if(p){
					cc[a[p]]++;
					if(cc[a[p]]>1 && (cc[a[p]]&1))now--;
					 else if(!(cc[a[p]]&1))now++;
				}
			w[i][j]=now;
		}	
	}

	for(int l,r;m;m--){
		
		read(l);read(r);
		l=(l+ans)%n+1; r=(r+ans)%n+1;
		if (l>r) swap(l,r);
	    int head=l/S+2,tail=r/S;
	    
	    //把中間整塊的求出累加; 
		if(head<=tail)ans=w[head][tail];
		 else ans=0;
		 
		//小範圍邊界; 
		int headt=min((int)(l/S+1)*S-1,n);if(head==tail+2)headt=r;
		int tailh=tail*S; if(head==tail+2)tailh=l;
		if(tailh==0)tailh=1;		

		//將數統計; 
		for(int j=l;j<=headt;j++)twice[a[j]]++;
		if(tailh>headt)//考慮同一塊的情況; 
		  for(int j=tailh;j<=r;j++)twice[a[j]]++;
		
		for(int j=l;j<=headt;j++)
		 if(twice[a[j]]){
				int t=0;
				if((head-1)<=tail)t=f[a[j]][tail]-f[a[j]][head-1];
				if(t){
					if((t&1) && (!((t+twice[a[j]])&1)))ans++;
					 else if((!(t&1)) && ((t+twice[a[j]])&1))ans--;
				}else if(!(twice[a[j]]&1))ans++;
			twice[a[j]]=0;
		 }
		 
		for(int j=tailh;j<=r;j++)
		 if(twice[a[j]]){
				int t=0;
				if((head-1)<=tail)t=f[a[j]][tail]-f[a[j]][head-1];
				if(t){
					if((t&1) && (!((t+twice[a[j]])&1)))ans++;
					 else if((!(t&1)) && ((t+twice[a[j]])&1))ans--;
				}else if(!(twice[a[j]]&1))ans++;
			twice[a[j]]=0;
			}
		printf("%d\n",ans);
	}
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章