CF848E Days of Floral Colours

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章