1. 程式人生 > >BZOJ4722: 由乃

BZOJ4722: 由乃

BZOJ

題意

給了你 n n 個數,你需要支援以下兩種操作:

  • 1   l  
    r : 1\ l\ r:
    l l r
    r
    這段區間中選兩個不相交的下標集合 A , B A,B ,一個集合的權值為下標對應的數的和,問是否能挑選出兩個權值相等的集合;
  • 2   l   r : 2\ l\ r: l l r r 這段區間中的數取立方;

對於每個 1 1 操作,如果能找到合法集合輸出Yuno,否則輸出Yuki;

題解

本題有個神奇的結論:當區間長度大於等於14時,必定能找到合法的兩個集合;怎麼理解這個結論呢,考慮將區間長度設為 l e n len ,那麼能夠選出的不同集合數為 2 l e n 2^{len} ,考慮最壞情況,這些集合將能夠取的值全部取完了,那麼一共能取的值有 l e n v len*v 個( v v 是所有數能夠取到的最大值),本題的 v = 1000 v=1000 ,列出不等式 2 l e n l e n v 2^{len}\geq len*v ,如果集合數大於了能夠取的值的數量,那麼必然能夠找到兩個合法的集合,此時解出 l e n = 14 len=14 ;接下來思路就很清晰了,對於每個詢問,如果詢問區間的長度大於等於14時直接輸出Yuno,要處理的就只有長度小於等於13的詢問了;因為長度很小,我們可以暴力列舉每個數選或者不選,但是這樣會有 2 13 2^{13} 的代價,無法接受,那麼我們運用 m e e t   i n   t h e   m i d d l e meet\ in\ the\ middle 的思想,暴力去列舉長度一半的數的取值情況,但是考慮到兩個集合都在前半段或者都在後半段,所以還要多考慮一種選出來的數加起來為0的情況,所以每次的代價就降為了 3 7 3^7 ;現在考慮最後修改的問題,區間取立方,看似非常棘手,但是我們可以通過儲存他立方了幾次用的時候臨時去算的方式,把問題就變簡單了,所需要算的東西為 A i 3 x A_i^{3^{x}} ,因為 v v 很小,這個東西可以通過預處理倍增陣列算,比如現在我們要算一個 A i 3 6 A_i^{3^{6}} ,我們可以先算出 g = A i 3 2 g=A_i^{3^{2}} ,然後算 g 3 4 g^{3^{4}} ,先預處理出 F [ i ] [ j ] F[i][j] 陣列,表示 i 3 j i^{3^{j}} , F [ i ] [ j ] = F [ F [ i ] [ j 1 ] ] [ j 1 ] F[i][j]=F[F[i][j-1]][j-1] ;

#include<bits/stdc++.h>
#define Fst first
#define Snd second
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
typedef unsigned int UI;
typedef unsigned long long ULL;
template<typename T> inline void read(T& x) {
	char c = getchar();
	bool f = false;
	for (x = 0; !isdigit(c); c = getchar()) {
		if (c == '-') {
			f = true;
		}
	}
	for (; isdigit(c); c = getchar()) {
		x = x * 10 + c - '0';
	}
	if (f) {
		x = -x;
	}
}
template<typename T, typename... U> inline void read(T& x, U& ... y) {
	read(x), read(y...);
}
const int N=1e5+10,MAX=1e4,mlog=17;
int n,Q,P,H,Len;
int W[20],vis[N],A[N],F[1010][mlog];
bool Flag;
struct BinaryIndexedTree {
	int X[N];
	int Lowbit(int a) {
		return a&-a;
	}
	void Modify(int l,int r) {
		while(l<=n) {
			++X[l];
			l+=Lowbit(l);
		}
		++r;
		while(r<=n) {
			--X[r];
			r+=Lowbit(r);
		}
	}
	int Query(int pos) {
		int res=0;
		while(pos) {
			res+=X[pos];
			pos-=Lowbit(pos);
		}
		return res;
	}
} BIT;
int Pow(int a,int k) {
	int res=1;
	for(int i=k;i;i>>=1) {
		if(i&1) res=res*a%P;
		a=a*a%P;
	}
	return res;
}
void DFS1(int cnt,int val,int g) {
	if(cnt==H+1) {
		if(!g) return;
		if(!val) Flag=true;
		if(val>0) vis[val]=Q;
		return;
	}
	DFS1(cnt+1,val+W[cnt],g+1); if(Flag) return;
	DFS1(cnt+1,val-W[cnt],g+1); if(Flag) return;
	DFS1(cnt+1,val,g);
}
void DFS2(int cnt,int val,int g) {
	if(cnt==Len+1) {
		if(!g) return;
		if(!val) Flag=true;
		if(val>0&&vis[val]==Q) Flag=true;
		return;
	}
	DFS2(cnt+1,val+W[cnt],g+1); if(Flag) return;
	DFS2(cnt+1,val-W[cnt],g+1); if(Flag) return;
	DFS2(cnt+1,val,g);
}
int Calc(int a,int k) {
	for(int i=0;i<mlog;++i) if(k>>i&1) a=F[a][i];
	return a;
}
int main() {
	read(n,Q,P);
	for(int i=0;i<P;++i) F[i][0]=i*i%P*i%P;
	for(int j=1;j<mlog;++j) {
		for(int i=0;i<P;++i)
			F[i][j]=F[F[i][j-1]][j-1];
	}
	for(int i=1;i<=n;++i) read(A[i]);
	while(Q--) {
		int opt,l,r; read(opt,l,r);
		if(opt==1) {
			if(r-l+1>=14) {
				puts("Yuno");
				continue;
			}
			if(l==r) {
				puts("Yuki");
				continue;
			}
			Len=r-l+1; H=Len/2;
			for(int i=l;i<=r;++i) W[i-l+1]=Calc(A[i],BIT.Query(i))+1;
			Flag=false; 
			DFS1(1,0,0); 
			DFS2(H+1,0,0);
			puts(Flag?"Yuno":"Yuki");
		}
		if(opt==2) BIT.Modify(l,r);
	}
	return 0;
}