为什么你现在叫 CyanSineSin 了??
ZJOI2022 Day2 的题都是非人力可及牛逼题目吗?好吓人啊!!!
先随便讲点暴力,因为这显然是线性变换,加上一些聊胜于无的特判就可以拿 50。
注意到有个奇怪的特殊限制,一个是
首先是
再考虑
经过多次手玩,记最大的
最后一段长度只有前面的一半。注意到形成这个形状的最大操作次数是
那么操作里面有个非常多余的东西就是除以二,这个除以二什么时候做都可以,可以从线性变换的角度考虑。那我们在最后除以
假设现在的
这个形式显得有规律可循,考虑差分。记之前的差分数组为
现在可以做到
这个数组的变换是非常有规律的,但是出现了
然后补充一个离谱的观察。这个置换拆成若干个置换环,满足大小最大的置换环是其他置换环大小的倍数……因此可以保证其循环节在
还能够优秀!考虑通过差分数组快速计算
首先第一个观察是,因为我们将除以二这个东西提到了最后,每次操作数列的权值和是不变的。那么求
记
(容易发现
我们要对所有的
那么我们将
至于循环卷积,在这个题中举一个例子出来。比如一个长为
同时对每个
注意到不同的置换环,如果大小相同造成贡献的位置也相同,可以提到一起做。这样的话大小不同的置换环个数只有
还有一点就是我们一直在讨论上面那个最后一段长度是前面的段的一半的 case,因此如果
当然根据我们上面那个比较离谱的观察,因为最大的置换环大小等于
注意到
细节比较多。完整代码戳这里。
int a[2000005],test;
int hst[25][2000005],b[2000005],d[2000005],m;
int cir[2000005];
bool vis[2000005];
int cbid[2000005];
int fcir[2000005];
void Solve()
{
int n, q, x;
long long k_max;
scanf("%d%d%d%lld", &n, &q, &x, &k_max);
int Sum=0;
for (int i = 1; i <= n; i ++) scanf("%d",&a[i]),Sum=Add(Sum,a[i]);
for(int i=1;i<=n;++i) hst[0][i]=a[i];
for(int i=1;i<=24;++i)
{
for(int j=1;j<=n;++j) b[j]=Add(hst[i-1][j],hst[i-1][n-j+1]);
for(int j=1;j<=n;++j) hst[i][j]=Mul(b[(j+1)/2],inv2);
}
if((n&(-n))==n)
{
LL ans=0;
for(int C=1;C<=q;++C)
{
LL k=rd(seed)%k_max;
k=min(k,24ll);
ans^=(LL(C)*hst[k][x]);
}
printf("%lld\n",ans);
return ;
}
int t=0;
{
int tmp=n;
while(tmp%2==0)
{
tmp>>=1;
++t;
}
++t;
}
int tp=t;
{
int pm=0;
for(int i=1;i<=n;i+=(1<<t)) b[++pm]=hst[t][i];
t=1<<t;
m=0;
for(int i=2;i<=pm;++i) d[++m]=Sub(b[i],b[i-1]);
for(int i=m;i;--i) d[++m]=MOD-d[i];
}
for(int i=1;i<=m;++i) cir[2*i%(m+1)]=i;
for(int i=1;i<=m;++i) vis[i]=false;
vector<Poly> Cir;
for(int i=1;i<=m;++i)
{
if(vis[i]) continue;
int u=i;
Cir.push_back({});
while(!vis[u])
{
vis[u]=true;
Cir.back().push_back(u);
u=cir[u];
}
}
int K=1;
for(auto st:Cir) K=max(len(st),K);
for(int i=0;i<=K;++i) cbid[i]=fcir[i]=0;
int inv=QuickPow(n);
vector<Poly> CirBuc;
for(auto st:Cir)
{
Poly A,B;
int len=len(st);
A.resize(len),B.resize(len);
for(int i=0;i<len;++i) A[i]=d[st[i]];
for(int i=0;i<len;++i) B[i]=MOD-Mul(inv,st[i]<=m/2?Sub(n,Mul(st[i],t)):0);
for(int i=0;i<len;++i) B[i]=Add(B[i],int(st[i]*t+1<=x));
reverse(B.begin(),B.end());
Poly C=A*B;
for(int i=0;i<len-1;++i) C[i]=Add(C[i],C[i+len]);
if(!cbid[len])
{
CirBuc.push_back({});
CirBuc.back().resize(len);
cbid[len]=len(CirBuc);
}
int id=cbid[len]-1;
for(int i=0;i<len;++i) CirBuc[id][i]=Add(CirBuc[id][i],C[i]);
}
for(int len=1;len<=K;++len)
{
if(cbid[len])
{
int id=cbid[len]-1;
for(int i=0,j=len-1;i<K;++i,j=(j+1)%len) fcir[i]=Add(fcir[i],CirBuc[id][j]);
}
}
LL ans=0;
for(int C=1;C<=q;++C)
{
LL k=rd(seed)%k_max;
int ret=0;
if(k<=tp) ret=hst[k][x];
else
{
k-=tp;
int coe=cp.Inv(k);
k%=K;
int s=Mul(fcir[k],coe);
ret=Add(s,Mul(inv,Sum));
}
ans^=LL(C)*LL(ret);
}
printf("%lld\n",ans);
}