BZOJ4722: 由乃

BZOJ

题意

给了你 n n n个数,你需要支持以下两种操作:

  • 1   l   r : 1\ l\ r: 1 l r: l l l r r r这段区间中选两个不相交的下标集合 A , B A,B A,B,一个集合的权值为下标对应的数的和,问是否能挑选出两个权值相等的集合;
  • 2   l   r : 2\ l\ r: 2 l r: l l l r r r这段区间中的数取立方;

对于每个 1 1 1操作,如果能找到合法集合输出Yuno,否则输出Yuki;

题解

本题有个神奇的结论:当区间长度大于等于14时,必定能找到合法的两个集合;怎么理解这个结论呢,考虑将区间长度设为 l e n len len,那么能够选出的不同集合数为 2 l e n 2^{len} 2len,考虑最坏情况,这些集合将能够取的值全部取完了,那么一共能取的值有 l e n ∗ v len*v lenv个( v v v是所有数能够取到的最大值),本题的 v = 1000 v=1000 v=1000,列出不等式 2 l e n ≥ l e n ∗ v 2^{len}\geq len*v 2lenlenv,如果集合数大于了能够取的值的数量,那么必然能够找到两个合法的集合,此时解出 l e n = 14 len=14 len=14;接下来思路就很清晰了,对于每个询问,如果询问区间的长度大于等于14时直接输出Yuno,要处理的就只有长度小于等于13的询问了;因为长度很小,我们可以暴力枚举每个数选或者不选,但是这样会有 2 13 2^{13} 213的代价,无法接受,那么我们运用 m e e t   i n   t h e   m i d d l e meet\ in\ the\ middle meet in the middle的思想,暴力去枚举长度一半的数的取值情况,但是考虑到两个集合都在前半段或者都在后半段,所以还要多考虑一种选出来的数加起来为0的情况,所以每次的代价就降为了 3 7 3^7 37;现在考虑最后修改的问题,区间取立方,看似非常棘手,但是我们可以通过保存他立方了几次用的时候临时去算的方式,把问题就变简单了,所需要算的东西为 A i 3 x A_i^{3^{x}} Ai3x,因为 v v v很小,这个东西可以通过预处理倍增数组算,比如现在我们要算一个 A i 3 6 A_i^{3^{6}} Ai36,我们可以先算出 g = A i 3 2 g=A_i^{3^{2}} g=Ai32,然后算 g 3 4 g^{3^{4}} g34,先预处理出 F [ i ] [ j ] F[i][j] F[i][j]数组,表示 i 3 j i^{3^{j}} i3j, F [ i ] [ j ] = F [ F [ i ] [ j − 1 ] ] [ j − 1 ] F[i][j]=F[F[i][j-1]][j-1] F[i][j]=F[F[i][j1]][j1];

#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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值