P3689 [ZJOI2017]多项式
既然都决定写了,为了以后的自己读得懂,还是写一篇详细的题解吧。
有点长,分个层。
快速幂
通读全题,第一个注意到的是大的不正常的
系数扩倍
接着可以注意到是在模 2 意义下进行,考虑快速幂的过程发现
为什么?把
由于有模 2,首先有
故
转移状态
然后我就不会了,但是可以发现
为了后文方便,令
显然,我们可以发现 轻松转移快速幂的。
更重要的是,我们发现这个东西对最后求答案很有帮助,但我们还不会转移。
快速幂转移
还是为了后文方便,对于开头不到
考虑某一段状态为
但如果我们将
如果你不想自己 yy 一个办法去重然后调边界致死的话,可以想办法将其一一对应进行计数。
我们对应取最后两个状态,即对于
总的来看也是正确的,设
然而还有个严重的问题就是快速幂不止乘 2,还可能加 1。
如果我们先转移了
嗯,
于是我们直接在这里确定乘上
这里我们采用的方法是取中间两个状态,即
总的来看应该有
同时最前面的这 随便地采取了
后缀和
转移就这样愉快的结束了,但你可能发现题目给的
由于快速幂转移过程中我们将前面
用题目中的形式表达一下,
于是我们可以得到
统计答案
显然地,我们用
统计的话由于
注意补东西都是在前面补,类比前面补 0,但这里不是补 0,而是补任意一种前缀。
以上。
哦不对还有我难看的代码,但好歹还有点注释,为了看得懂我多截了一点:
const int Mx=100;
const int M=1<<19;
int n,m,mx,mxx,trs[2][2][M];
LL T,L,R,stf,stg,alf,alg;
int tt,flg[Mx+5];
LL len[Mx+5],F[2][M];
inline LL Ask(LL v)
{
int i,t,f,fl;
LL k=T,l,dl,sm;
LLL prf,prg;
//前 T 位偷懒用 __int128 存的
for(;k^1;k>>=1)
{
flg[++tt]=k&1;
len[tt]=tt-1?(len[tt-1]+1)>>1:v;
//要求到 [v,N] 的 F 每次要的长度
}
for(i=0;i<=n;F[0][(stf<<(mx-(i++)))&alf]++);
for(t=1,f=0,l=n,prf=stf<<(mx+1);tt;tt--,t^=1,f^=1)
{
fl=flg[tt];
for(i=0;i<=alf;i++)
{
if((k=F[f][i]))
{
F[t][trs[fl][0][i]]+=k;F[t][trs[fl][1][i]]+=k;
F[f][i]=0;
//用预处理好的直接转移
}
}
for(i=prg=0;i<=mxx;i++)
{
prg|=((prf>>i)&1)<<(i<<1);
}
//暴力转移前 T 位
dl=Max(0ll,(l=(l<<1)+(fl?mx:0))-len[tt]);
//该掐多长
if(fl)
{
for(i=prf=0;i<=mx;i++)
{
if((stf>>i)&1)
{
prf^=prg<<i;
}
}
if(dl<mx)for(i=dl;i<mx;F[t][(prf>>(mxx+mx+mx-(i++)+1))&alf]++);
if(mx<dl)for(i=mx;i<dl;F[t][(prf>>(mxx+mx+mx-(i++)+1))&alf]--);
//掐和加上 n 种状态二合二(我毒瘤)
prf=(prf>>(mxx+mx-dl))&alg;
}
else
{
for(i=0;i<dl;F[t][(prg>>(mxx+mx-(i++)+1))&alf]--);
//掐
prf=(prg>>(mxx-dl))&alg;
}
l-=dl;
}
for(i=sm=0;i<(1<<(mx-m));sm+=F[f][(i++)|stg]);
//补前缀统计
for(i=0;i<=alf;F[f][i++]=0);
return sm;
}
inline int gt()
{
char ch;
for(ch=gc();ch^48&&ch^49;ch=gc());
return ch^48;
}
int TT;
signed main()
{
#ifndef ONLINE_JUDGE
// freopen("poly1.in","r",stdin);
freopen("_.in","r",stdin);
// freopen("_.out","w",stdout);
#endif
int i,j;
LL k,kk;
for(TT=read();TT;TT--)
{
n=read();read(T);m=read()-1;read(L);read(R);mx=Max(n,m);mxx=(mx<<1)+1;
//题解对应代码 n->n m->T K->m t->mx T->mxx
for(i=stf=0;i<=n;i++)
{
stf|=gt()<<(mx-i);
}
for(i=stg=0;i<=m;i++)
{
stg|=gt()<<(mx-i);
}
alf=(1<<(mx+1))-1;alg=(1ll<<(mxx+1))-1;
for(i=0;i<=alf;i++)
{
for(j=k=0;j<=mx;j++)
{
k|=((i>>j)&1ll)<<(j<<1);
}
trs[0][0][i]=k>>mx;trs[0][1][i]=(k>>(mx-1))&alf;
//扩倍转移的预处理
for(j=kk=0;j<=mx;j++)
{
if((stf>>j)&1)
{
kk^=k<<j;
}
}
trs[1][0][i]=(kk>>mx)&alf;trs[1][1][i]=(kk>>(mx-1))&alf;
//扩倍加一转移的预处理
writenum(R-L+1<m?0:Ask(n*T+1-L)-Ask(n*T+1-(R-m+1)),10);
//毒瘤
}
return output;
}