(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

Catalog

@

Problem:传送门

 原题目描述在最下面。

 对给定的式子算解。

 \(0\leq k\leq n+m,c_k=(\sum_{i+j=k}i\times j\times \sigma_{a_i,b_j}) mod\;998244353\),其中\(当且仅当a=b时,\sigma_{a_i,b_j}=1。\)

Solution:

 我们发现只有当\(a_i\)和\(b_j\)相等时才会对答案造成贡献。

 一共\(2e^5\)个数字,我们枚举每一个数字算贡献,同时分情况讨论:当这个

数字出现次数小于阀值\(T\)时,我们\(O(n^2)\)暴力算;当出现次数大于\(T\)时,我们用\(FFTorNTT\)计算。

 听说这题有人\(FFT\)丢精度就\(wa\)了,我就干脆用\(NTT\)写了,刚好这个模数也是费马质数嘛,接下来看怎么把上述奇怪的卷积转化成一个可以用\(FFT\)和\(NTT\)计算的卷积。

 我们把\(a\)数组和\(b\)数组中数字\(x\)所有出现的位置提出来放到新数组里面去,比如\(x\)出现在\(a\)的\(1,3,4\)位置,也出现在\(b\)的\(2,4,5,6\)位置。

 当\(k\)等于\(5\)时,数字\(x\)产生的贡献\(1*4,2*3\)

 当\(k\)等于\(6\)时,数字\(x\)产生的贡献\(1*5\)

 我们知道一般的卷积式子是长这样的:\(C_k=\sum_{i+j=k}A_i\times B_j\)

 哇哦,感觉这个式子和那个贡献好相似啊。

 我们就构造这样两个数组\(A_{a_i}=a_i,B_{b_j}=b_j\)可以得到:

\(A[]=\{0,1,0,3,4\},B[]=\{0,0,2,0,4,5,6\}\)

 我们对这两个数组求他们多项式乘法的结果,然后将结果的贡献累计到答案数组中就行啦。

 然后当阀值\(T\)为\(10000\)时,复杂度就可以承受了。

AC_Code:

感谢日天的板子

这题复杂度的分析dls讲题时分析的很清楚啦,一位群友的笔记截图在下面

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
namespace lh {
#define o2(x) (x)*(x)
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, LL> pii;
} using namespace lh;
const int MX = 2e5 + 5;
//const int P = (479 << 21) + 1;
const int P = 998244353;
const int MOD = 998244353;
const int G = 3;
const int NUM = 20;
struct my_NTT {
LL wn[NUM];
LL a[MX << 1], b[MX << 1];
LL pow (LL a, LL x, LL mod) {
LL ans = 1;
a %= mod;
while (x) {
if (x & 1) ans = ans * a % mod;
x >>= 1;
a = a * a % mod;
}
return ans;
}
//在程序的开头就要放
void init() {
for (int i = 0; i < NUM; i++) {
int t = 1 << i;
wn[i] = pow (G, (P - 1) / t, P);
}
}
void Rader (LL F[], int len) {
int j = len >> 1;
for (int i = 1; i < len - 1; i++) {
if (i < j) swap (F[i], F[j]);
int k = len >> 1;
while (j >= k) j -= k, k >>= 1;
if (j < k) j += k;
}
}
void NTT (LL F[], int len, int t) {
Rader (F, len);
int id = 0;
for (int h = 2; h <= len; h <<= 1) {
id++;
for (int j = 0; j < len; j += h) {
LL E = 1;
for (int k = j; k < j + h / 2; k++) {
LL u = F[k];
LL v = E * F[k + h / 2] % P;
F[k] = (u + v) % P;
F[k + h / 2] = (u - v + P) % P;
E = E * wn[id] % P;
}
}
}
if (t == -1) {
for (int i = 1; i < len / 2; i++) swap (F[i], F[len - i]);
LL inv = pow (len, P - 2, P);
for (int i = 0; i < len; i++) F[i] = F[i] * inv % P;
}
}
void Conv (LL a[], LL b[], int len) {
NTT (a, len, 1);
NTT (b, len, 1);
for (int i = 0; i < len; i++) a[i] = a[i] * b[i] % P;
NTT (a, len, -1);
}
void gao (LL A[], LL B[], int n, int m, LL ans[]) {//0~n-1
int len = 1;
while (len < n + m) len <<= 1;
for (int i = 0; i < n; i++) a[i] = A[i];
for (int i = 0; i < m; i++) b[i] = B[i];
for (int i = n; i < len; i++) a[i] = 0;
for (int i = m; i < len; i++) b[i] = 0;
Conv (a, b, len);
for (int i = 0; i < len; i++) ans[i] = (ans[i]+a[i])%MOD;
}
}ntt;
const int MXN = 2e5 + 5;
int n, m;
int ar[MXN], br[MXN];
LL A[MXN], B[MXN];
std::vector<int> all[MXN], bll[MXN];
LL ans[MXN];
void solve1(int id) {
for(int i = 0; i < all[id].size(); ++i) {
for(int j = 0; j < bll[id].size(); ++j) {
ans[all[id][i]+bll[id][j]] += (LL)all[id][i] * bll[id][j];
ans[all[id][i]+bll[id][j]] %= MOD;
}
}
}
void solve2(int id) {
for(int i = 0; i <= n+m; ++i) A[i] = B[i] = 0;
for(int i = 0; i < all[id].size(); ++i) A[all[id][i]] = all[id][i];
for(int i = 0; i < bll[id].size(); ++i) B[bll[id][i]] = bll[id][i];
ntt.gao(A, B, all[id].back()+1, bll[id].back()+1, ans);
}
int main(int argc, char const *argv[]) {
scanf("%d%d", &n, &m); ++n, ++m;
ntt.init();
std::vector<int> vs;
for(int i = 0; i < n; ++i) scanf("%d", &ar[i]), vs.push_back(ar[i]);
for(int i = 0; i < m; ++i) scanf("%d", &br[i]), vs.push_back(br[i]);
sort(vs.begin(), vs.end());
vs.erase(unique(vs.begin(), vs.end()), vs.end());
for(int i = 0, tmp; i < n; ++i) {
tmp = lower_bound(vs.begin(), vs.end(), ar[i]) - vs.begin();
all[tmp].push_back(i);
}
for(int i = 0, tmp; i < m; ++i) {
tmp = lower_bound(vs.begin(), vs.end(), br[i]) - vs.begin();
bll[tmp].push_back(i);
}
for(int i = 0; i < vs.size(); ++i) {
if(all[i].size() + bll[i].size() <= 10000) solve1(i);
else solve2(i);
}
for(int i = 0; i <= n + m-2; ++i) printf(i!=n+m-2?"%lld ":"%lld\n", ans[i]);
return 0;
}

Problem Description:

果然,我的$FFTwa了$
```cpp
#include
#define fi first
#define se second
#define pb push_back
namespace lh {
#define o2(x) (x)*(x)
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair pii;
}

using namespace lh;

const int MX = 2e5 + 5;

//const int P = (479 << 21) + 1;

const int P = 998244353;

const int MOD = 998244353;

const int G = 3;

const int NUM = 20;

struct my_FFT {

const double pi = acos (-1.0);

static const int MM = MX * 4;

int len, res[MM], mx; //开大4倍

struct cpx {

double r, i;

cpx (double r = 0, double i = 0) : r (r), i (i) {};

cpx operator+ (const cpx &b) {return cpx (r + b.r, i + b.i);}

cpx operator- (const cpx &b) {return cpx (r - b.r, i - b.i);}

cpx operator* (const cpx &b) {return cpx (rb.r - ib.i,i*b.r + r * b.i);}

} va[MM], vb[MM];

void rader (cpx F[], int len) { //len = 2^M,reverse F[i] with F[j] j为i二进制反转

int j = len >> 1;

for (int i = 1; i < len - 1; ++i) {

if (i < j) swap (F[i], F[j]); // reverse

int k = len >> 1;

while (j >= k) j -= k, k >>= 1;

if (j < k) j += k;

}

}

void FFT (cpx F[], int len, int t) {

rader (F, len);

for (int h = 2; h <= len; h <<= 1) {

cpx wn (cos (-t * 2 * pi / h), sin (-t * 2 * pi / h) );

for (int j = 0; j < len; j += h) {

cpx E (1, 0); //旋转因子

for (int k = j; k < j + h / 2; ++k) {

cpx u = F[k];

cpx v = E * F[k + h / 2];

F[k] = u + v;

F[k + h / 2] = u - v;

E = E * wn;

}

}

}

if (t == -1) //IDFT

for (int i = 0; i < len; ++i) F[i].r /= len;

}

void Conv (cpx a[], cpx b[], int len) { //求卷积

FFT (a, len, 1);

FFT (b, len, 1);

for (int i = 0; i < len; ++i) a[i] = a[i] * b[i];

FFT (a, len, -1);

}

void gao (LL a[], LL b[], int n, int m, LL ans[]) {

len = 1;

mx = n + m;

while (len <= mx) len <<= 1; //mx为卷积后最大下标

for (int i = 0; i < len; i++) va[i].r =va[i].i = vb[i].r = vb[i].i = 0;

for (int i = 0; i < n; i++) va[i].r = a[i];//根据题目要求改写

for (int i = 0; i < m; i++) vb[i].r = b[i];//根据题目要求改写

Conv (va, vb, len);

for (int i = 0; i < len; ++i) ans[i] = (ans[i]+(LL)(va[i].r + 0.5))%MOD;

}

}fft;

const int MXN = 2e5 + 5;

int n, m;

int ar[MXN], br[MXN];

LL A[MXN], B[MXN];

std::vector all[MXN], bll[MXN];

LL ans[MXN*10];

void solve1(int id) {

for(int i = 0; i < all[id].size(); ++i) {

for(int j = 0; j < bll[id].size(); ++j) {

ans[all[id][i]+bll[id][j]] += (LL)all[id][i] * bll[id][j];

ans[all[id][i]+bll[id][j]] %= MOD;

}

}

}

void solve2(int id) {

for(int i = 0; i <= n+m; ++i) A[i] = B[i] = 0;

for(int i = 0; i < all[id].size(); ++i) A[all[id][i]] = all[id][i];

for(int i = 0; i < bll[id].size(); ++i) B[bll[id][i]] = bll[id][i];

fft.gao(A, B, all[id].back()+1, bll[id].back()+1, ans);

}

int main(int argc, char const *argv[]) {

scanf("%d%d", &n, &m); ++n, ++m;

std::vector vs;

for(int i = 0; i < n; ++i) scanf("%d", &ar[i]), vs.push_back(ar[i]);

for(int i = 0; i < m; ++i) scanf("%d", &br[i]), vs.push_back(br[i]);

sort(vs.begin(), vs.end());

vs.erase(unique(vs.begin(), vs.end()), vs.end());

for(int i = 0, tmp; i < n; ++i) {

tmp = lower_bound(vs.begin(), vs.end(), ar[i]) - vs.begin();

all[tmp].push_back(i);

}

for(int i = 0, tmp; i < m; ++i) {

tmp = lower_bound(vs.begin(), vs.end(), br[i]) - vs.begin();

bll[tmp].push_back(i);

}

for(int i = 0; i < vs.size(); ++i) {

if(all[i].size() + bll[i].size() <= 10000) solve1(i);

else solve2(i);

}

for(int i = 0; i <= n + m-2; ++i) printf(i!=n+m-2?"%lld ":"%lld\n", ans[i]);

return 0;

}

Wannafly Winter Camp Day5 Div1 E题 Fast Kronecker Transform 转化为NTT或FFT的更多相关文章

  1. Wannafly Winter Camp Day8(Div1,onsite) E题 Souls-like Game 线段树 矩阵乘法

    目录 Catalog Solution: (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 Catalog @ Problem:传送门  Portal  原题目描述在最下面.  简单的 ...

  2. CCPC-Wannafly Winter Camp Day5 Div1 - Sorting - [线段树]

    题目链接:https://zhixincode.com/contest/22/problem/I?problem_id=314 样例输入 1 5 9 31 5 3 2 41 1 52 1 51 1 1 ...

  3. 2019 wannafly winter camp

    2019 wannafly winter camp Name Rank Solved A B C D E F G H I J K day1 9 5/11 O O O O O day2 5 3/11 O ...

  4. 线段树优化建图(cf787d, 2019Wannafly Winter Camp Day7 Div1 E)

    线段树优化建图,用于区间到区间建边时降低空间复杂度 建立两颗线段树,一颗in, 代表进入这个区间,一颗out,代表从这个区间出去 in树从父亲向儿子建边,代表宏观进入整个区间,不向下寻找 out树从儿 ...

  5. 2019 wannafly winter camp day5-8代码库

    目录 day5 5H div2 Nested Tree (树形dp) 5F div2 Kropki (状压dp) 5J div1 Special Judge (计算几何) 5I div1 Sortin ...

  6. 2019 wannafly winter camp day 3

    2019 wannafly winter camp day 3 J 操作S等价于将S串取反,然后依次遍历取反后的串,每次加入新字符a,当前的串是T,那么这次操作之后的串就是TaT.这是第一次转化. 涉 ...

  7. 2020 CCPC Wannafly Winter Camp Day1 C. 染色图

    2020 CCPC Wannafly Winter Camp Day1 C. 染色图 定义一张无向图 G=⟨V,E⟩ 是 k 可染色的当且仅当存在函数 f:V↦{1,2,⋯,k} 满足对于 G 中的任 ...

  8. CCPC-Wannafly Winter Camp Day5 (Div2, onsite)

    Replay: Dup4: 时间复杂度算不对? 一点点思路不经过验证就激动的要死? 浪费自己一个小时还浪费别人一个小时? 对1e3不敏感? 1e3 * 1e3是多少? 模拟建边跑dp不写非要写个大模拟 ...

  9. Wannafly Winter Camp 2019.Day 8 div1 E.Souls-like Game(线段树 矩阵快速幂)

    题目链接 \(998244353\)写成\(99824435\)然后调这个线段树模板1.5h= = 以后要注意常量啊啊啊 \(Description\) 每个位置有一个\(3\times3\)的矩阵, ...

随机推荐

  1. cf round#598 CDEF

    C:模拟:未跳到目的地之前先贪心放板子,能到达目的地后紧贴着放板子 先判能不能跳到目的地,能跳到再考虑是否需要将后面的板子往前移动 #include<bits/stdc++.h> usin ...

  2. CDN技术之--集群服务与负载均衡

    Web集群是由多个同时运行同一个web应用的服务器组成,在外界看来就像一个服务器一样,这多台服务器共同来为客户提供更高性能的服务.集群更标准的定义是:一组相互独立的服务器在网络中表现为单一的系统,并以 ...

  3. %各位大佬的博客.tql

    线性基:https://www.cnblogs.com/ljh2000-jump/p/5869991.html#4219854 数位DP  https://blog.csdn.net/jk211766 ...

  4. display和visibility

    display: none; visibility: hidden; 相同点:两者都是隐藏元素不同点:display不保留位置,visibility保留位置

  5. 四. jenkins部署springboot项目(1)--window环境

    前提:jenkins和springboot运行在同一台机器 springboot项目使用git和maven jenkins所需的插件如Maven,Git等这里就不再详述. 1.jenkins配置git ...

  6. MySQL高级学习笔记(一):mysql简介、mysq linux版的安装(mysql 5.5)

    文章目录 MySQL简介 概述 mysql高手是怎样炼成的 mysq linux版的安装(mysql 5.5) 下载地址 拷贝&解压缩 检查工作 检查当前系统是否安装过mysql 检查/tmp ...

  7. 11.Jmeter 快速入门教程 -- jmeter事务控制器

    你肯定知道, jmeter是一个跨系统平台的性能测试工具, 比如他可以在linux,freebsd,windows,solaris 等等各种系统上可以运行. 我可以说, 事务 transaction ...

  8. Django框架(二十三)—— Django rest_framework-解析器

    解析器 一.解析器的作用 根据请求头 content-type 选择对应的解析器对请求体内容进行处理,将传过来的数据解析成字典 二.使用解析器 1.局部使用 在视图类中重定义parser_classe ...

  9. 洛谷 P2023 维护序列——线段树

    先上一波题目 https://www.luogu.org/problem/P2023 复习了一波线段树 题目涉及的操作有区间加 区间乘以及区间求和 tips:线段树在传标记的时候 优先传乘法标记再传加 ...

  10. 一份详尽的 Java 问题排查工具清单,值得收藏!

    | grep 5 -A 3    #上匹配seq 10 | grep 5 -B 3    #下匹配seq 10 | grep 5 -C 3    #上下匹配,平时用这个就妥了cat f.txt | g ...