链接:https://codeforces.com/problemset/problem/848/E
被拿来做训练题,两个小时码出n<=25爆搜30分。。。
带图更详细的做法可以直接看官方题解:https://codeforces.com/blog/entry/54233
这里讲下大致思路:
环的问题一般很难直接考虑,所以考虑从简单的链入手,考虑一段左右是相对的连,中间还有x对没连的状况,记为f0(x),发现可以分成子问题,一种是x个里没有相对连非常好算(可以预处理一个g数组表示无相对的连法,其实是一个隔两个的斐波那契数列),一种是有相对的连,并且相对的左右相邻位置没有跨越相连的,可以递归到f0(x),还有一种是有相对的连且左右相邻位置跨越相连的,需要新定义一个f1(x)去处理一段左右相对的连,且一边靠相连点的点被跨越连,中间剩下x个点的情况。类似f0的处理可以算f1(x)。
(这两个数组推出来后爆算n^2,但显然可以分治fft)
之后考虑如何统计答案,假设钦点1,n+1一定是相对连,那么可以简单处理没有其它相对连的(要考虑旋转相对连的),
然后考虑枚举第二个相对连的位置(2 to n),发现不考虑旋转的话,考虑分出来四段连法的四种情况,3种都可以用f0,f1处理,只有一种要定义一个新的f2处理一段左右相对的连,且两边靠相连点的点都被跨越连,中间剩下x个点的情况。算法与之前类似。
最后考虑如何旋转统计,发现我们这样枚举第二个相对位置记为i,2~i-1间没有其它相对的,它旋转要重复统计的就是逆时针把第二个相对的位置转0~i-1,个人理解它就是在枚举跨越1的两个相对点对的之间段大小、位置,可以做到不重不漏,因为如果第二个相对位置转大于等于1,会在其它i的枚举里被统计。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=998244353;
const int N=4e5+100;
ll Add(ll x,ll y)
{return (x+y)%mod;}
ll Sub(ll x,ll y)
{return (x-y+mod)%mod;}
ll Mul(ll x,ll y)
{return x*y%mod;}
ll qpow(ll x,ll y=mod-2)
{
ll res=1;
while(y)
{
if(y&1)res=Mul(res,x);
x=Mul(x,x),y>>=1;
}
return res;
}
ll F[N],G[N];
int wh[N],len,cc;
void ntt(ll *a,bool inv)
{
for(int i=1;i<len;i++)
if(i<wh[i])swap(a[i],a[wh[i]]);
ll tp,mo,ha;
for(int l=2,md;l<=len;l<<=1)
{
md=l>>1,tp=qpow(3,(mod-1)/l);
for(int i=0;i<len;i+=l)
{
mo=1;
for(int j=0;j<md;j++,mo=Mul(mo,tp))
{
ha=Mul(a[i+j+md],mo);
a[i+j+md]=Sub(a[i+j],ha);
a[i+j]=Add(a[i+j],ha);
}
}
}
if(inv)
{
tp=qpow(len);
for(int i=1;i<len/2;i++)swap(a[i],a[len-i]);
for(int i=0;i<len;i++)a[i]=Mul(a[i],tp);
}
}
void pre_ntt(int le)
{
cc=0,len=1;
while(len<=le)++cc,len<<=1;
for(int i=1;i<len;i++)
wh[i]=(wh[i>>1]>>1)|((i&1)<<(cc-1));
}
void clr()
{for(int i=0;i<len;i++)F[i]=G[i]=0;}
int n;
ll f0[N],f1[N],f2[N],g[N];
void sol1(int l,int r)
{
if(l==r)
{
f0[l]=Add(f0[l],Mul(g[l],Mul(l,l)));
f1[l]=Add(f1[l],Mul(g[l],Mul(l+1,l+1)));
return;
}
int mid=(l+r)>>1;
sol1(l,mid);
pre_ntt(mid-l+1+r-l+1);
clr();//bg f0
for(int i=l;i<=mid;i++)
F[i-l]=f0[i];
for(int i=0;i<=r-l;i++)
G[i]=Mul(g[i],Mul(i,i));
ntt(F,0),ntt(G,0);
for(int i=0;i<len;i++)F[i]=Mul(F[i],G[i]);
ntt(F,1);
for(int i=mid+1;i<=r;i++)
if(i-l-1>=0)f0[i]=Add(f0[i],F[i-l-1]);
clr();
for(int i=l;i<=mid;i++)
F[i-l]=f1[i];
for(int i=0;i<=r-l;i++)
G[i]=Mul(g[i],Mul(i+1,i+1));
//f(l==0&&r==3)cerr<<F[0]<<' '<<G[0]<<'\n';
ntt(F,0),ntt(G,0);
for(int i=0;i<len;i++)F[i]=Mul(F[i],G[i]);
ntt(F,1);
//if(l==0&&r==3)cerr<<F[0]<<'\n';
for(int i=mid+1;i<=r;i++)
if(i-l-3>=0)f0[i]=Add(f0[i],F[i-l-3]);
clr();//bg f1
for(int i=l;i<=mid;i++)
F[i-l]=f0[i];
for(int i=0;i<=r-l;i++)
G[i]=Mul(g[i],Mul(i+1,i+1));
ntt(F,0),ntt(G,0);
for(int i=0;i<len;i++)F[i]=Mul(F[i],G[i]);
ntt(F,1);
for(int i=mid+1;i<=r;i++)
if(i-l-1>=0)f1[i]=Add(f1[i],F[i-l-1]);
clr();
for(int i=l;i<=mid;i++)
F[i-l]=f1[i];
for(int i=0;i<=r-l;i++)
G[i]=Mul(g[i],Mul(i+2,i+2));
ntt(F,0),ntt(G,0);
for(int i=0;i<len;i++)F[i]=Mul(F[i],G[i]);
ntt(F,1);
for(int i=mid+1;i<=r;i++)
if(i-l-3>=0)f1[i]=Add(f1[i],F[i-l-3]);
sol1(mid+1,r);
}
void sol2(int l,int r)
{
if(l==r)
{
f2[l]=Add(f2[l],Mul(g[l],Mul(l+2,l+2)));
return;
}
int mid=(l+r)>>1;
sol2(l,mid);
pre_ntt(mid-l+1+r-l+1);
clr();//bg f2
for(int i=l;i<=mid;i++)
F[i-l]=f1[i];
for(int i=0;i<=r-l;i++)
G[i]=Mul(g[i],Mul(i+1,i+1));
ntt(F,0),ntt(G,0);
for(int i=0;i<len;i++)F[i]=Mul(F[i],G[i]);
ntt(F,1);
for(int i=mid+1;i<=r;i++)
if(i-l-1>=0)f2[i]=Add(f2[i],F[i-l-1]);
clr();
for(int i=l;i<=mid;i++)
F[i-l]=f2[i];
for(int i=0;i<=r-l;i++)
G[i]=Mul(g[i],Mul(i+2,i+2));
//f(l==0&&r==3)cerr<<F[0]<<' '<<G[0]<<'\n';
ntt(F,0),ntt(G,0);
for(int i=0;i<len;i++)F[i]=Mul(F[i],G[i]);
ntt(F,1);
//if(l==0&&r==3)cerr<<F[0]<<'\n';
for(int i=mid+1;i<=r;i++)
if(i-l-3>=0)f2[i]=Add(f2[i],F[i-l-3]);
sol2(mid+1,r);
}
signed main()
{
cin>>n;
g[0]=g[2]=1;
for(int i=4;i<=n;i++)g[i]=Add(g[i-2],g[i-4]);
sol1(0,n),sol2(0,n);
ll ans=0,res;
ans=Add(ans,1LL*(n-1)*(n-1)%mod*g[n-1]%mod*n%mod);
ans=Add(ans,1LL*(n-1)*(n-1)%mod*g[n-3]%mod*n%mod);
// cout<<f0[1]<<'\n';
for(int x=0;x<=n-2;x++)
{
res=0;
res=Add(res,Mul(Mul(Mul(x,x),g[x]),f0[n-x-2]));
// if(res)cerr<<x<<' '<<res<<'\n';
if(n-x-3>=0&&x>=1)res=Add(res,2LL*x*x%mod*g[x-1]%mod*f1[n-x-3]%mod);
// if(res)cerr<<x<<' '<<res<<'\n';
if(n-x-4>=0&&x>=2)res=Add(res,1LL*x*x%mod*g[x-2]%mod*f2[n-x-4]%mod);
// if(res)cerr<<x<<' '<<res<<'\n';
ans=Add(ans,Mul(res,x+1));
// if(res)cerr<<x<<' '<<res<<'\n';
}
printf("%lld\n",ans);
return 0;
}
ps:这题在官方题解还有nlogn求逆做法,在洛谷题解有40*n的爆推式子求导做法,然后xza大佬又在官方题解写了bm求线性齐次递推式的log级别做法...