Codeforces1096G Lucky Tickets(NTT优化dp)

设\(f[i][j]\)表示填了\(i\)个数,数位和为\(j\)的方案数

于是方程为:
\[f[i][j]=\sum_{k=1}^9 f[i-1][j-k]*[CanUse[k]==1]\]

其中\(CanUse[i]\)表示是否可用\(i\)这个数字

最终答案为:
\[\sum_{i=1}^{9*(n/2)}f[n/2][j]\]

直接转移肯定\(T\)飞,需要一些优化。于是我们观察到这个式子是卷积形式的式子,直接上\(NTT\)板子+快速幂即可

\(P.S.\)一些优化

  1. 可以把每个\(pow(g,(mod-1)/i)\)和\(pow(g,-(mod-1)/i)\)预处理出来,\(NTT\)过程中直接调用
  2. 快速幂前可以直接把系数表示化为点值表示,快速幂时直接乘,快速幂后再换回去系数表示

所以时间复杂度为\(0(9*(n/2)log_29*(n/2))\)

代码:

#include <bits/stdc++.h>
#define ll long long
#define mod 998244353ll
#define N 1048580
using namespace std;
ll G[N],F[N],tmp[N],tot[N],invG[N],inv,ans=0;
const ll g=3;
int rev[N],len,x;
void NTT(ll *a,int n,int f){
    int i,j,k;
    for(i=0;i<n;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(i=2;i<=n;i<<=1){
        ll omega;
        int m=i>>1;
        (f==1)?omega=G[i]:omega=invG[i];
        for(j=0;j<n;j+=i){
            ll p=1;
            for(k=j;k<j+m;++k,(p*=omega)%=mod){
                ll u=a[k],v=p*a[k+m]%mod;
                a[k]=(u+v)%mod,a[k+m]=(u-v+mod)%mod;
            }
        }
    }
    if(f==-1) for(i=0;i<n;++i) (a[i]*=inv)%=mod;
}
ll ksm(ll x,int y){
    ll ans=1;
    while(y){
        if(y&1) (ans*=x)%=mod;
        (x*=x)%=mod;
        y>>=1;
    }
    return ans;
}
void init(int n){
    int i;
    for(len=1,x=0;len<=n;len<<=1,++x);
    for(i=0;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(x-1));
    G[len]=ksm(g,(mod-1)/len),invG[len]=ksm(g,mod-1-(mod-1)/len);
    for(i=(len>>1);i>=2;i>>=1){
        G[i]=G[i<<1]*G[i<<1]%mod;
        invG[i]=invG[i<<1]*invG[i<<1]%mod;
    }
}
void mul(ll *a,ll *b){
    for(int i=0;i<len;++i) a[i]=a[i]*b[i]%mod;
}
int main(){
    int n,d,i;
    scanf("%d%d",&n,&d);n/=2;
    for(i=1;i<=d;++i) scanf("%d",&x),F[x]=1;
    init(9*n);
    tot[0]=1;inv=ksm(1ll*len,mod-2);
    NTT(F,len,1),NTT(tot,len,1);
    x=n;
    while(x){
        if(x&1) mul(tot,F);
        x>>=1;
        if(x) mul(F,F);
    }
    NTT(tot,len,-1);
    for(i=0;i<=9*n;++i) (ans+=tot[i]*tot[i]%mod)%=mod;
    cout<<ans;
}

原文地址:https://www.cnblogs.com/PsychicBoom/p/10351620.html

时间: 2024-08-30 04:06:40

Codeforces1096G Lucky Tickets(NTT优化dp)的相关文章

ZOJ 3874 Permutation Graph (分治NTT优化DP)

题面:vjudge传送门 ZOJ传送门 题目大意:给你一个排列,如果两个数构成了逆序对,就在他们之间连一条无向边,这样很多数会构成一个联通块.现在给出联通块内点的编号,求所有可能的排列数 推来推去容易发现性质,同一联通块内的点一定是连续标号的,否则无解 然后我就不会了 好神的$NTT$优化$DP$啊 根据上面的性质,联通块之间是互不影响的,所以我们对每个联通块分别统计答案再相乘 定义$f[i]$表示$i$个点构成的合法联通块,可能的排列数 一个合法联通块的所有元素一定在同一联通块内,说明不可能存

HDU 5322 Hope (分治NTT优化DP)

题面传送门 题目大意: 假设现在有一个排列,每个数和在它右面第一个比它大的数连一条无向边,会形成很多联通块. 定义一个联通块的权值为:联通块内元素数量的平方. 定义一个排列的权值为:每个联通块的权值之积 求长度为$n$所有排列的权值之和,$n\leq 1e5$,$1e4$组询问 原题面描述不清楚啊..害得我白想了30min 和ZOJ3874一样都是排列$DP$问题 $DP$方程还是不难想的 假设现在有一个$i-1$的排列,当我们把$i$某个位置上时 $i$前面的数都会和$i$连通,$i$后面的数

HDU 5279 YJC plays Minecraft (分治NTT优化DP)

题目传送门 题目大意:有$n$个小岛,每个小岛上有$a_{i}$个城市,同一个小岛上的城市互相连接形成一个完全图,第$i$个小岛的第$a_{i}$个城市和第$i+1$个小岛的第$1$个城市连接,特别地,第$n$个小岛的第$a_{n}$个城市和第$1$个小岛的第$1$个城市连接.现在要断掉图中的一些边,保证任意两个城市只有一条路径或者不连通,求合法的断边方案总数,$n,a_{i}<=1e5$ 完全不会(喷血 我们对每个小岛单独讨论 如果任意两个城市只有一条路径或者不连通,那么这张图只能是一个森林

Gym 100418J Lucky tickets(数位dp)

题意:给定一个n,求区间[1, n]之间的所有的a的个数,a满足: a能整除  把a表示自身二进制以后1的个数 思路:题意很绕.... 数位dp,对于所有可能的1的个数我们都dfs一次 对于某一个可能的1的个数p来说,状态dp(len, i, j, k)里的每一位分别表示当前位,当前确定位的值模除p,已经有了多少个1,是否已经小于给定的n, 注意的是这题范围很大,要用unsigned long long 来定义n #include<cstdio> #include<cstring>

Lucky Tickets URAL - 1036 dp+大数

用b[i][j]表示递推到第i位时数字和为j的方案数,那么总方案数就是b[n][s/2]的平方 1 n, s = input().split(' ') 2 3 n = int(n) 4 s = int(s) 5 6 if s % 2 == 1: 7 print(0) 8 else: 9 s = int(s // 2) 10 b = [[]] 11 a = [] 12 for i in range(0, 10): 13 a.append(1) 14 for i in range(10, s + 1

DP+高精度 URAL 1036 Lucky Tickets

题目传送门 1 /* 2 题意:转换就是求n位数字,总和为s/2的方案数 3 DP+高精度:状态转移方程:dp[cur^1][k+j] = dp[cur^1][k+j] + dp[cur][k]; 4 高精度直接拿JayYe的:) 5 异或运算的规则: 6 0⊕0=0,0⊕1=1 7 1⊕0=1,1⊕1=0 8 口诀:相同取0,相异取1 9 */ 10 #include <cstdio> 11 #include <cstring> 12 #include <string>

HDU-5332(前缀和优化dp/CDQ+NTT)

HDU-5332(CDQ+NTT/前缀和优化dp) 考虑依次求出\(i\)个点的答案 假设当前有\(i-1\)个点,枚举第\(i\)个点前面的点数\(j\),则\(dp_i=dp_{i-j-1}\cdot (j+1)^2\cdot C(i-1,i-j-1)\cdot j!\) 直接转移是\(O(n^2)\)的,可以看到是一个\(dp\)转移与差值有关,所以可以用\(CDQ\)分治+\(NTT\)解决 关于这种简单粗暴的做法,模板题HDU-5730 题解 以下是暴力的代码 #include<bit

Ural 1036 Lucky Tickets

Lucky Tickets Time Limit: 2000ms Memory Limit: 16384KB This problem will be judged on Ural. Original ID: 103664-bit integer IO format: %lld      Java class name: (Any) You are given a number 1 ≤ N ≤ 50. Every ticket has its 2N-digit number. We call a

bzoj-4518 4518: [Sdoi2016]征途(斜率优化dp)

题目链接: 4518: [Sdoi2016]征途 Description Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地.除第m天外,每一天晚上Pine都必须在休息站过夜.所以,一段路必须在同一天中走完. Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小. 帮助Pine求出最小方差是多少. 设方差是v,可以证明,v×m^2是一个整数.为了避免精度误差,输出结果时输出v×m^2. In