【洛谷5527】[Ynoi2012] NOIP2016人生巅峰(抽屉原理+暴力)

点此看题面

大致题意: 给定一个序列\(\{a_i\}\),支持两种操作:询问是否能在一个区间中选出两个无交集的非空下标集合,使得\(\sum(a_i+1)\)的值相等;将一个区间内的\(a_i\)全部修改为\(a_i^3\ mod\ v\)

抽屉原理

首先,虽然题目要求下标集合无交集,但是如果两个值相等的集合有交集,我们完全可以同时消去交集部分。因此这个限制可以直接忽略。

假设一个长度为\(l\)的区间可能无解,由于每个数可以选或不选,共有\(2^l-1\)种选数方案,而无解也就要求每种选数方案得到的数两两不同,即:

\[2^l-1>1000\times l \]

得出\(l\le 13\)

也就是说,我们只需考虑区间长度小于等于\(13\)的情况即可。

那么直接暴搜似乎就可以?(常数小跑不满

不过如果你一定要保证复杂度,可以去写\(Meet\ in\ Middle\),反正我直接暴力水过了。。。

修改

考虑如何处理区间立方的操作。

显然容易维护出一个数被立方操作的次数,每次询问前用扩展欧拉定理即可求出每个数的值。

写到这里突然发现我原先的做法写复杂了,我原本是直接维护一个数的指数,这样还要判断指数是否超过过\(\phi(v)\),非常麻烦。。。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define V 1000
using namespace std;
int n,v,p,ti,sz,a[N+5],s[N+5],bl[N+5],pw[V+5][2*V+5],vis[N*V+5];
int f[N+5],ff[N+5],g[N+5],gg[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define D isdigit(c=tc())
		char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
}F;
I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
I void GetPhi(RI x)//求phi
{
	RI i;for(p=1,i=2;i*i<=x;++i) if(!(x%i)) {p*=i-1,x/=i;W(!(x%i)) p*=i,x/=i;}x^1&&(p*=x-1);
}
I bool dfs(CI x,CI y,CI t=0,CI f=0)//暴搜
{
	if(f) {if(vis[t]==ti) return 1;vis[t]=ti;}if(x>y) return 0;
	if(dfs(x+1,y,t,0)) return 1;return dfs(x+1,y,t+s[x],1);
}
int main()
{
	RI Qt,i,j;F.read(n),F.read(Qt),F.read(v),GetPhi(v);
	for(i=0;i^v;++i) for(pw[i][0]=j=1;j<=2*p;++j) pw[i][j]=pw[i][j-1]*i%v;//预处理幂
	for(sz=sqrt(n),i=1;i<=n;++i) F.read(a[i]),f[i]=g[bl[i]=(i-1)/sz+1]=1;
	RI op,x,y;W(Qt--) switch(F.read(op),F.read(x),F.read(y),op)
	{
		case 1:if(y-x+1>=14) {puts("Yuno");break;}for(++ti,i=x;i<=y;++i)//长度过长必然有解
				s[i]=pw[a[i]][f[i]*g[bl[i]]%p+(gcd(a[i],v)^1&&(ff[i]||gg[bl[i]]||f[i]*g[bl[i]]>=p)?p:0)]+1;//扩展欧拉定理求值
			puts(dfs(x,y)?"Yuno":"Yuki");break;//暴搜验证
		case 2:if(bl[x]==bl[y]) {for(i=x;i<=y;++i) (f[i]*=3)>=p&&(f[i]%=p,ff[i]=1);break;}//Ynoi怎么能不写分块。。。
			for(i=bl[x]*sz;i>=x;--i) (f[i]*=3)>=p&&(f[i]%=p,ff[i]=1);
			for(i=(bl[y]-1)*sz+1;i<=y;++i) (f[i]*=3)>=p&&(f[i]%=p,ff[i]=1);
			for(i=bl[x]+1;i^bl[y];++i) (g[i]*=3)>=p&&(g[i]%=p,gg[i]=1);break;
	}return 0;
}
posted @ 2020-06-09 21:05  TheLostWeak  阅读(17)  评论(0编辑  收藏