https://codeforc.es/contest/1096/problem/G

把数组分成前后两半,那么前半部分的各个值的表示方案的平方的和就是答案。

这些数组好像可以dp出来。

一开始设dp[i]数组表示1<<i位的各个值,那么做16次NTT然后把n分解下去就求出来答案了。总共要30多次convolution,比较大的NTT少说有6次。

后来发现dp数组可以只记录一次,用完就可以接着用。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; const int MAXN = 5e6, mod = 998244353; inline int pow_mod(ll x, int n) {
ll res;
for(res = 1; n; n >>= 1, x = x * x % mod)
if(n & 1)
res = res * x % mod;
return res;
} inline int add_mod(int x, int y) {
x += y;
return x >= mod ? x - mod : x;
} inline int sub_mod(int x, int y) {
x -= y;
return x < 0 ? x + mod : x;
} void NTT(int a[], int n, int op) {
for(int i = 1, j = n >> 1; i < n - 1; ++i) {
if(i < j)
swap(a[i], a[j]);
int k = n >> 1;
while(k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
for(int len = 2; len <= n; len <<= 1) {
int g = pow_mod(3, (mod - 1) / len);
for(int i = 0; i < n; i += len) {
int w = 1;
for(int j = i; j < i + (len >> 1); ++j) {
int u = a[j], t = 1ll * a[j + (len >> 1)] * w % mod;
a[j] = add_mod(u, t), a[j + (len >> 1)] = sub_mod(u, t);
w = 1ll * w * g % mod;
}
}
}
if(op == -1) {
reverse(a + 1, a + n);
int inv = pow_mod(n, mod - 2);
for(int i = 0; i < n; ++i)
a[i] = 1ll * a[i] * inv % mod;
}
} int A[MAXN + 5], B[MAXN + 5]; int pow2(int x) {
int res = 1;
while(res < x)
res <<= 1;
return res;
} void convolution(int a[], int b[], int asize, int bsize, int c[], int &csize) {
int n = pow2(asize + bsize - 1);
for(int i = 0; i < n; ++i) {
A[i] = i < asize ? a[i] : 0;
B[i] = i < bsize ? b[i] : 0;
}
NTT(A, n, 1);
NTT(B, n, 1);
for(int i = 0; i < n; ++i)
A[i] = 1ll * A[i] * B[i] % mod;
NTT(A, n, -1);
csize = n;
for(int i = 0; i < n; ++i)
c[i] = A[i];
return;
} int dp[MAXN], dpsize; //dp[i]:1<<i位能表示的各个位数
int ans[MAXN], anssize; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
cout<<(1<<20)<<endl;
int n, k;
scanf("%d%d", &n, &k);
for(int i = 0; i < k; ++i) {
int tmp;
scanf("%d", &tmp);
dp[tmp] = 1;
}
dpsize = 10;
int n2 = n >> 1;
bool fi = true;
while(n2) {
if(n2 & 1) {
if(!fi)
convolution(ans, dp, anssize, dpsize, ans, anssize);
else {
fi = false;
for(int j = 0; j < dpsize; j++)
ans[j] = dp[j];
anssize = dpsize;
}
}
n2 >>= 1;
if(n2)
convolution(dp, dp, dpsize, dpsize, dp, dpsize);
}
ll res = 0;
for(int i = 0; i < anssize; i++) {
res += 1ll * ans[i] * ans[i] % mod;
}
printf("%lld\n", res % mod);
return 0;
}

但其实不需要用到convolution???

注意到convolution的本质其实是用NTT变成点值,然后用点值加法再用NTT变回来。

其实注意到

void convolution(int a[], int b[], int asize, int bsize, int c[], int &csize) {
int n = pow2(asize + bsize - 1);
for(int i = 0; i < n; ++i) {
A[i] = i < asize ? a[i] : 0;
B[i] = i < bsize ? b[i] : 0;
}
NTT(A, n, 1);
NTT(B, n, 1);
for(int i = 0; i < n; ++i)
A[i] = 1ll * A[i] * B[i] % mod;
NTT(A, n, -1);
csize = n;
for(int i = 0; i < n; ++i)
c[i] = A[i];
return;
}

里面,两个多项式卷积实际上只是对应位置做乘法。

那么只要把对应位置的乘法一次全部做完就可以了。

从另一个角度想,要是把这个数组本身视作一个多项式(生成函数),\(a_ix^i\)中,\(a_i\)就是和为\(i\)的方案数。那么

\(F(x)=\sum\limits_{i=0}^{9}a_ix^i\) 就是1位数的选法,选n次那就是\(F^n(x)\)

这个就直接点值化之后对点值直接快速幂然后再插值 。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; const int MAXN = 1<<21, mod = 998244353; inline int pow_mod(ll x, int n) {
ll res;
for(res = 1; n; n >>= 1, x = x * x % mod)
if(n & 1)
res = res * x % mod;
return res;
} inline int add_mod(int x, int y) {
x += y;
return x >= mod ? x - mod : x;
} inline int sub_mod(int x, int y) {
x -= y;
return x < 0 ? x + mod : x;
} void NTT(int a[], int n, int op) {
for(int i = 1, j = n >> 1; i < n - 1; ++i) {
if(i < j)
swap(a[i], a[j]);
int k = n >> 1;
while(k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
for(int len = 2; len <= n; len <<= 1) {
int g = pow_mod(3, (mod - 1) / len);
for(int i = 0; i < n; i += len) {
int w = 1;
for(int j = i; j < i + (len >> 1); ++j) {
int u = a[j], t = 1ll * a[j + (len >> 1)] * w % mod;
a[j] = add_mod(u, t), a[j + (len >> 1)] = sub_mod(u, t);
w = 1ll * w * g % mod;
}
}
}
if(op == -1) {
reverse(a + 1, a + n);
int inv = pow_mod(n, mod - 2);
for(int i = 0; i < n; ++i)
a[i] = 1ll * a[i] * inv % mod;
}
} int pow2(int x) {
int res = 1;
while(res < x)
res <<= 1;
return res;
} int A[MAXN + 5]; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int n, k;
scanf("%d%d", &n, &k);
for(int i = 0; i < k; ++i) {
int tmp;
scanf("%d", &tmp);
A[tmp] = 1;
}
n>>=1;
int maxn=pow2(n*9);
NTT(A,maxn,1);
for(int i = 0; i < maxn; i++)
A[i]=pow_mod(A[i],n);
NTT(A,maxn,-1);
ll res=0;
for(int i = 0; i < maxn; i++)
res += 1ll * A[i] * A[i] % mod;
printf("%lld\n", res % mod);
return 0;
}

Codeforces - 1096G - Lucky Tickets - NTT的更多相关文章

  1. 2019.01.26 codeforces 1096G. Lucky Tickets(生成函数)

    传送门 题意简述:现在有一些号码由000~999中的某些数字组成(会给出),号码总长度为nnn,问有多少个号码满足前n2\frac n22n​个数码的和等于后n2\frac n22n​个数码的和(保证 ...

  2. @codeforces - 1096G@ Lucky Tickets

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 已知一个数(允许前导零)有 n 位(n 为偶数),并知道组成这个 ...

  3. Codeforces 1096G. Lucky Tickets【生成函数】

    LINK 题目大意 很简单自己看 思路 考虑生成函数(为啥tags里面有一个dp啊) 显然,每一个指数上是否有系数是由数集中是否有这个数决定的 有的话就是1没有就是0 然后求出这个生成函数的\(\fr ...

  4. Codeforces1096G Lucky Tickets(NTT优化dp)

    设\(f[i][j]\)表示填了\(i\)个数,数位和为\(j\)的方案数 于是方程为: \[f[i][j]=\sum_{k=0}^9 f[i-1][j-k]*[CanUse[k]==1]\] 其中\ ...

  5. CF1096. G. Lucky Tickets(快速幂NTT)

    All bus tickets in Berland have their numbers. A number consists of n digits (n is even). Only k dec ...

  6. Codeforces Gym 100418J Lucky tickets 数位DP

    Lucky ticketsTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/view ...

  7. POJ-2346 Lucky tickets(线性DP)

    Lucky tickets Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 3298 Accepted: 2174 Descrip ...

  8. DP+高精度 URAL 1036 Lucky Tickets

    题目传送门 /* 题意:转换就是求n位数字,总和为s/2的方案数 DP+高精度:状态转移方程:dp[cur^1][k+j] = dp[cur^1][k+j] + dp[cur][k]; 高精度直接拿J ...

  9. Ural 1036 Lucky Tickets

    Lucky Tickets Time Limit: 2000ms Memory Limit: 16384KB This problem will be judged on Ural. Original ...

随机推荐

  1. 安装SQL2012出现[HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD)设置为 1

    本人安装SQL2012出现这个错误,找了三天三夜,终于把问题找出来,共享给有需要的人们,不用重新换系统 错误如下: 1,此问题是系统.net Framework版本冲突,首先下载.net Framew ...

  2. node.js入门学习(一)环境安装,REPL,fs模块,path模块,http模块

    一.node.js介绍 1.1.node.js是什么 官网首页总结:Node.js® 是一个基于 Chrome V8 引擎 的 JavaScript 运行时. 1)node.js是一个开发平台,就像j ...

  3. UVa 1601 || POJ 3523 The Morning after Halloween (BFS || 双向BFS && 降维 && 状压)

    题意 :w*h(w,h≤16)网格上有n(n≤3)个小写字母(代表鬼).要求把它们分别移动到对应的大写字母里.每步可以有多个鬼同时移动(均为往上下左右4个方向之一移动),但每步结束之后任何两个鬼不能占 ...

  4. Devexpress MVC DropDownList (持续更新))

    @Html.DevExpress().DropDownEdit(settings => { settings.Name = "psBankCharge"; settings. ...

  5. 【转载】What is the difference between authorized_keys and known_hosts file for SSH?

    The known_hosts file lets the client authenticate the server, to check that it isn't connecting to a ...

  6. 【转】Django restful framework中自动生成API文档

    转自 https://www.cnblogs.com/sui776265233/p/11350434.html 自动生成api文档(不管是函数视图还是类视图都能显示) 1.安装rest_framewo ...

  7. 【canvas学习笔记四】绘制文字

    本节我们来学习如何绘制文字. 绘制文字有两个主要的方法: fillText(text, x, y [, maxWidth]) 在x, y位置填充文字text,有一个可选参数maxWidth设置最大绘制 ...

  8. Win7,win10(部分机型) 安装appscan9.0.3.10(可升级)实操流程

    Win10部分机型不能很好的兼容appscan,建议使用者用win7系统安装appscan 写于:2018.12.2 IBM Security AppScan Standard 可通过自动执行应用安全 ...

  9. [CSP-S模拟测试]:括号密码(贪心)

    题目描述 在“无限神机”的核心上,有一个奇怪的括号密码,密码初始已经有一个括号序列,有$n$个限制条件,每个限制条件描述为$l_i$和$r_i$,表示区间$[l_i,r_i]$的括号序列必须合法.调整 ...

  10. ali之mtl平台学习

    摩天轮平台可以进行无线测试.设备借用.打包发布.线上监控等功能. 无线测试包括:mock测试.真机适配.代码审查.验收报告等. mock测试类似于fiddler,主要用于接口查看,可以查看请求,返回串 ...