CF848E Days of Floral Colours

89 篇文章 0 订阅
72 篇文章 0 订阅

链接: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级别做法...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值