鏈接: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級別做法...