NOI模擬 學園祭的旅行【線段樹合併】【二進制拆分】

傳送門

好題,學到了二進制查詢的一種思想。

首先這個異或最大值我們會想到0/1trie,本來合併0/1trie是可以的,但是發現權值會變,然後有點頭禿。

換個想法。不妨一開始把所有數到根的前綴和都插進去,然後查詢的時候記錄一個delta就能獲知在trie上對應的位置。

再一想,這樣的區間查詢不如使用線段樹。

 

使用啓發式合併,我們維護重兒子,每次先搜索重兒子,然後讓輕兒子合併進來。

對於查詢,遍歷子樹,將每個點的權值丟到當前線段樹裏查詢ans打擂臺。

記得離散化。

警示:記住位運算的優先級是最低的,,多打括號。。

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define int long long
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;ch=getchar();
	}return cnt*f; 
}
char ch[1000003];
int n,s[1000003];
int mul[1000003];
int pos[1000003],cnt;
int fac[1000003];
int ans;
const int mod=998244853;
int ksm(int a,int b){
	int sum=1;
	while(b){
		if(b&1)sum=sum*a%mod;a=a*a%mod;b>>=1;
	}return sum;
}
signed main(){
	n=in;scanf("%s",ch+1);for(int i=1;i<=n;i++)s[i]=ch[i]-'0';
	for(int i=1;i<=n;i++){
		if(s[i]==1)pos[++cnt]=i,mul[cnt]=pos[cnt]-cnt;
	}mul[0]=1;
	for(int i=2;i<=cnt;i++)mul[i]=mul[i-1]*mul[i]%mod;
	fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
//	cout<<fac[0]<<" "<<pos[1]<<" "<<mul[1]<<endl;
//	for(int i=1;i<=3;i++)cout<<fac[i]<<" ";cout<<endl;
//	for(int i=1;i<=cnt;i++)cout<<pos[i]<<" ";cout<<endl;
//	for(int i=1;i<=cnt;i++)cout<<mul[i]<<" ";cout<<endl;
	for(int i=1;i<=cnt;i++){
		ans=(ans+fac[n-i]*(n-pos[i]+1)%mod*mul[i-1]%mod*pos[i])%mod;
	}//cout<<ans<<endl;
	
	ans=(ans+mul[cnt]*fac[n-cnt]%mod*n%mod)%mod;
//	cout<<ans<<endl;
	cout<<ans*ksm(fac[n],mod-2)%mod;
	return 0;
}

 

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