【LuoguP4887】第十四分塊(前體)

題目鏈接

題意

區間兩數異或在二進制下有 kk11 的對數。

Sol

普通莫隊的話,如果要實時維護好區間內的答案需要支持區間對一個數求答案。
直接做不是很好做,容易發現其實這也就是一個區間詢問。那麼可以把莫隊中要求的東西再次離線下來。
我們把上述詢問拆成前綴相減的形式,這樣我們要做到就是多次詢問一個前綴對一個數的答案。
由於在數據範圍下二進制下有 kk11 的數並不是太多,我們可以直接從前往後做,遇到一個數 xx 則把 xnumber(k)x\oplus number(k) 加入桶中 (number(k)number(k)表示二進制下有 kk11的數)。當我們遇到一個詢問 數 yy 和當前前綴的答案時,只需要看 yy 的桶被加入了多少個數就行了。

複雜度分析: 由於詢問個數和莫隊移動次數同階,爲 O(nn)O(n\sqrt n) ,查詢複雜度是 O(1)O(1) 的所以查詢複雜度就是 O(nn)O(n\sqrt n)。然後我們每加入一個數需要 最多(147){14\choose 7}次插入操作,所以這部分複雜度爲 O(n(147))O(n*{14\choose 7})

愉快地提交上去後,就會 TLE+MLETLE+MLE,因爲這個複雜度還是比較緊的,而且詢問個數 O(nn)O(n\sqrt n) 全存下來的話會被卡空間。
怎麼辦呢?

發現有許多詢問都是一個前綴和前綴後面那一個數的答案,這部分我們可以直接一開始預處理然後詢問時直接貢獻。省去存儲一些詢問,加快了速度。

但是這樣還不夠,再發現我們的另一種詢問是對於一個固定的端點的詢問一段區間內的數,我們可以只存一個詢問。這樣不僅省去了莫隊時指針的移動複雜度,也省了空間,就可以過了。

code:

#include<bits/stdc++.h>
using namespace std;
#define Set(a,b) memset(a,b,sizeof(a))
template<class T>inline void init(T&x){
	x=0;char ch=getchar();bool t=0;
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	if(t) x=-x;return;
}typedef long long ll;
int n,m,k;
const int N=1e5+10;
int SIZE;
const int INF=16384;
int a[N],bl[N];ll ans[N];
struct query{
	int l,r,id;
	query(){l=r=id=0;}
	query(int _l,int _r,int _id){l=_l,r=_r,id=_id;}
	inline bool operator <(const query b)const{if(bl[l]!=bl[b.l]) return bl[l]<bl[b.l];else return r<b.r;}
}Que[N];
typedef pair<int,int> PA;
vector<query> PQ[N];
int number[3434];int numcnt=0;
#define lowbit(a) ((a)&(-a))
inline int Count(int x){int cnt=0;while(x) ++cnt,x^=lowbit(x);return cnt;}
int Pre[N],Bac[INF];ll S[N];

int main()
{
	init(n),init(m),init(k);SIZE=sqrt(n);
	for(int i=1;i<=n;++i) init(a[i]),bl[i]=(i-1+SIZE)/SIZE;
	for(int i=0;i<INF;++i) if(Count(i)==k) number[++numcnt]=i;
	
	if(k>14) while(m--) puts("0");
	else {
		S[0]=0;
		for(int i=1;i<=n;++i) {Pre[i]=Bac[a[i]];S[i]=S[i-1]+Pre[i];for(int j=1;j<=numcnt;++j) ++Bac[a[i]^number[j]];}S[n+1]=S[n];
		Set(Bac,0);
		for(int i=1;i<=m;++i) {init(Que[i].l),init(Que[i].r),Que[i].id=i;}sort(Que+1,Que+1+m);
		int l=Que[1].l;int r=Que[1].l;
		for(int i=1;i<=m;++i) {
			const int ID=Que[i].id;
			if(r<Que[i].r) {++r;ans[ID]+=S[Que[i].r]-S[r-1];PQ[l-1].emplace_back(query(r,Que[i].r,-ID));r=Que[i].r;}
			if(l>Que[i].l) {--l;ans[ID]-=(S[l]-S[Que[i].l-1])+(int)(k==0)*(l-Que[i].l+1);PQ[r].emplace_back(query(Que[i].l,l,ID));l=Que[i].l;}
			if(r>Que[i].r) {ans[ID]-=S[r]-S[Que[i].r];PQ[l-1].emplace_back(query(Que[i].r+1,r,ID));r=Que[i].r;}
			if(l<Que[i].l) {ans[ID]+=S[Que[i].l-1]-S[l-1]+(int)(k==0)*(Que[i].l-l);PQ[r].emplace_back(query(l,Que[i].l-1,-ID));l=Que[i].l;}
		}
		Set(Bac,0);
		for(int i=1;i<=n;++i) {
			for(int j=1;j<=numcnt;++j) ++Bac[a[i]^number[j]];
			for(query P:PQ[i]) {
				int ID=abs(P.id),f=P.id/ID;
				int l=P.l,r=P.r;ll res=0;
				for(int j=l;j<=r;++j) res+=Bac[a[j]];
				ans[ID]+=res*f;
			}
		}
		for(int i=1;i<=m;++i) ans[Que[i].id]+=ans[Que[i-1].id];
		for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
	}
	return 0;
}

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