【洛谷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;
}
待到再迷茫时回头望,所有脚印会发出光芒