bzoj千题计划309:bzoj4332: JSOI2012 分零食(分治FFT)

https://www.lydsy.com/JudgeOnline/problem.php?id=4332

因为如果一位小朋友得不到糖果,那么在她身后的小朋友们也都得不到糖果。

所以设g[i][j] 表示前i位小朋友,分到j个糖果,且前i位小朋友都分到糖果的方案数

令F(x) 表示分到x个糖果的欢乐程度

∴g[i][j] = ∑ g[i-1][j-k]*F(k)

记g[i]=g[i-1]*F,则 g[i]=F ^ i

但是要求的是 Σ g[i][m]

记f[n]=Σ g[i]  i∈[1,n] ,那么ans=f[n][m]

f[n]=Σ g[i]  i∈[1,n]

=Σ f(n/2)+Σ g[i]  i∈[n/2+1,n]

=Σ f(n/2)+Σ F^i  i∈[n/2+1,n]

=Σ f(n/2)+Σ F^(n/2+i)  i∈[1,n/2]

=Σ f(n/2)+F^(n/2) * Σ F^i  i∈[1,n/2]

=Σ f(n/2)+g(n/2)*f(n/2)

然后可以分治FFT解决

如果n是奇数,f(n)=f(n-1)+g[n]=f(n-1)+g(n-1)*f

边界条件:g[][0]=1

#include<cmath>
#include<cstdio>
#include<algorithm>

using namespace std;

const int M=1<<17;

#define N 10001

int m,mod;

int r[M+1];
int len;

const double pi=acos(-1);

struct Complex
{
    double x,y;
    Complex() { }
    Complex(double x_,double y_):x(x_),y(y_) { }
    Complex operator + (Complex p)
    {
        Complex C;
        C.x=x+p.x;
        C.y=y+p.y;
        return C;
    }
    Complex operator - (Complex p)
    {
        Complex C;
        C.x=x-p.x;
        C.y=y-p.y;
        return C;
    }
    Complex operator * (Complex p)
    {
        Complex C;
        C.x=x*p.x-y*p.y;
        C.y=x*p.y+y*p.x;
        return C;
    }
    void clear()
    {
        x=y=0;
    }
};

typedef Complex E;

E F[M+1],f[M+1],g[M+1],tmp[M+1];

void FFT(E *a,int ty)
{
    for(int i=0;i<len;++i)
        if(i<r[i]) swap(a[i],a[r[i]]);
    for(int i=1;i<len;i<<=1)
    {
        E wn(cos(pi/i),ty*sin(pi/i));
        for(int p=i<<1,j=0;j<len;j+=p)
        {
            E w(1,0);
            for(int k=0;k<i;++k,w=w*wn)
            {
                E x=a[j+k],y=w*a[i+j+k];
                a[j+k]=x+y; a[i+j+k]=x-y;
            }
        }
    }
    if(ty==-1)
    {
        for(int i=0;i<len;++i) a[i].x=a[i].x/len,a[i].x=int(a[i].x+0.5)%mod,a[i].y=0;
    }
}

void solve(E *f,E *g,int n)
{
    if(!n)
    {
        g[0].x=1;
        return;
    }
    if(n&1)
    {
        solve(f,g,n-1);
        FFT(g,1);
        for(int i=0;i<len;++i) g[i]=g[i]*F[i];
        FFT(g,-1);
        for(int i=0;i<=m;++i) f[i]=f[i]+g[i];
        for(int i=0;i<=m;++i) f[i].x=int(f[i].x)%mod,f[i].y=0;
        for(int i=m+1;i<len;++i) f[i].clear(),g[i].clear();
    }
    else
    {
        solve(f,g,n/2);
        for(int i=0;i<len;++i) tmp[i]=f[i];
        FFT(tmp,1);
        FFT(g,1);
        for(int i=0;i<len;++i) tmp[i]=tmp[i]*g[i];
        FFT(tmp,-1);
        for(int i=0;i<len;++i) g[i]=g[i]*g[i];
        FFT(g,-1);
        for(int i=0;i<=m;++i) f[i]=f[i]+tmp[i];
        for(int i=0;i<=m;++i) f[i].x=int(f[i].x)%mod,f[i].y=0;
        for(int i=m+1;i<len;++i) f[i].clear(),g[i].clear();
    }
}

int main()
{
    int n,o,s,u;
    scanf("%d%d%d%d%d%d",&m,&mod,&n,&o,&s,&u);
    //F[0].x=1;
    for(int i=1;i<=m;++i) F[i].x=(o*i*i+s*i+u)%mod;
    int l=0;
    for(len=1;len<=m+m;len<<=1,l++);
    for(int i=0;i<len;++i) r[i]=(r[i>>1]>>1)|((i&1)<<l-1);
    FFT(F,1);
    solve(f,g,n);
    printf("%d",int(f[m].x));
    return 0;
}

原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8960712.html

时间: 2024-08-02 20:37:52

bzoj千题计划309:bzoj4332: JSOI2012 分零食(分治FFT)的相关文章

【BZOJ 4332】 4332: JSOI2012 分零食 (FFT+快速幂)

4332: JSOI2012 分零食 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 119  Solved: 66 Description 这里是欢乐的进香河,这里是欢乐的幼儿园. 今天是2月14日,星期二.在这个特殊的日子里,老师带着同学们欢乐地跳着,笑着.校长从幼儿园旁边的小吃店买了大量的零食决定分给同学们.听到这个消息,所有同学都安安静静地排好了队,大家都知道,校长不喜欢调皮的孩子. 同学们依次排成了一列,其中有A位小朋友,有三个共同的欢乐

bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic

http://www.lydsy.com/JudgeOnline/problem.php?id=1018 关键点在于只有两行 所以一个2*m矩形连通情况只有6种 编号即对应代码中的a数组 线段树维护 用b数组表示 节点第0/1行的最右一列是否连接了右边 来 辅助 节点的合并 查询 对两个点位于矩形的位置分4种情况讨论 两点是否联通,要考虑四种情况 (以两个位置是矩形左上角和右上角为例) 1.直接联通,线段树的节点包含了这种情况,直接判断 2. 3. 4. 后三种情况需要再查询[1,l]和[r,n

bzoj千题计划115:bzoj1024: [SCOI2009]生日快乐

http://www.lydsy.com/JudgeOnline/problem.php?id=1024 枚举横着切还是竖着切,一边儿分多少块 #include<cstdio> #include<algorithm> using namespace std; double S; double dfs(double x,double y,int n) { if(n==1) return max(x,y)/min(x,y); double ans=1e9; for(int i=1;i&

bzoj千题计划153:bzoj2431: [HAOI2009]逆序对数列

http://www.lydsy.com/JudgeOnline/problem.php?id=2431 dp[i][j] 表示i的排列,有j个逆序对的方案数 加入i+1,此时i+1是排列中最大的数, 所以放在i+1后面的所有数都会与i+1形成逆序对 转移方程:dp[i][j]=Σ dp[i-1][j-k]  k∈[0,min(j,i-1)] 前缀和优化 朴素的DP #include<cstdio> #include<algorithm> using namespace std;

bzoj千题计划185:bzoj1260: [CQOI2007]涂色paint

http://www.lydsy.com/JudgeOnline/problem.php?id=1260 区间DP模型 dp[l][r] 表示涂完区间[l,r]所需的最少次数 从小到大们枚举区间[l,r] 如果col[l]==col[r] dp[l][r]=min(dp[l+1][r],dp[l][r-1],dp[l+1][r-1]+1) 否则 dp[l][r]=min(dp[l][k]+dp[k+1][r]) 我还是辣鸡啊~~~~(>_<)~~~~,这种题都不能秒 #include<c

bzoj千题计划239:bzoj4069: [Apio2015]巴厘岛的雕塑

http://www.lydsy.com/JudgeOnline/problem.php?id=4069 a!=1: 从高位到低位一位一位的算 记录下哪些位必须为0 dp[i][j] 表示前i个数分为j组,这一位为0,且满足之前必须为0的位也是0 是否可行 枚举k,表示k+1~i分为一组 若k+1~i的和满足 必须为0的位是0,且dp[k][j-1] 为true 则dp[i][j]为true a=1: 从高位到低位一位一位的算 记录下哪些位必须为0 dp[i] 表示前i个数,这一位为0,且满足之

bzoj千题计划245:bzoj1095: [ZJOI2007]Hide 捉迷藏

http://www.lydsy.com/JudgeOnline/problem.php?id=1095 查询最远点对,带修改 显然可以用动态点分治 对于每个点,维护两个堆 堆q1[x] 维护 点分树x的子树中,所有黑点到x的点分树中父节点的距离 堆q2[x]维护 点分树x的子节点的堆q1的堆顶,即若y是x在点分树中的子节点,则q2[x].push(q1[y].top()) 再来维护一个全局的堆Q,维护所有q2的堆顶,即Q.push(q2[x].top()) #include<cmath> #

bzoj千题计划252:bzoj1095: [ZJOI2007]Hide 捉迷藏

http://www.lydsy.com/JudgeOnline/problem.php?id=1095 点分树+堆 请去看 http://www.cnblogs.com/TheRoadToTheGold/p/8463436.html 线段树维护括号序列 对树进行dfs,入栈时加一个左括号,出栈时加一个右括号,那么书上两点间的距离=括号序列两点间不匹配括号数 例: 树1--2--3,2为根 括号序列为 (2(3)(1)) 2和1的距离 为 ()( = 1, 3和1的距离为 )( =2 具体怎么维

bzoj千题计划255:bzoj3572: [Hnoi2014]世界树

http://www.lydsy.com/JudgeOnline/problem.php?id=3572 明显需要构造虚树 点属于谁管理分三种情况: 1.属于虚树的点 2.在虚树上的边上的点 3.既不属于虚树的点,又不属于虚树上的边的点 第一种情况: 先做一遍树形dp,得到子树中距离它最近的点 再dfs一遍,看看父节点那一块 是否有比它现在的点更近的点 第二种情况: 一条边u-->v 如果u和v属于同一点x管理,那么这条边所代表的所有点也都属于x管理 否则的话,二分一个点tmp,tmp以上的点归