Jomoo的模板
by Jomoo
目录
1 杂类算法
1.1 快读模板
template <typename Int>
inline Int read()
{
Int flag = 1;
char c = getchar();
while ((!isdigit(c)) && c != '-') c = getchar();
if (c == '-') flag = -1, c = getchar();
Int init = c & 15;
while (isdigit(c = getchar())) init = (init << 3) + (init << 1) + (c & 15);
return init * flag;
}
template <typename Int>
inline Int read(char &c)
{
Int flag = 1;
c = getchar();
while ((!isdigit(c)) && c != '-') c = getchar();
if (c == '-') flag = -1, c = getchar();
Int init = c & 15;
while (isdigit(c = getchar())) init = (init << 3) + (init << 1) + (c & 15);
return init * flag;
}
template <typename Int>
inline void write(Int x)
{
if (x < 0) putchar('-'), x = ~x + 1;
if (x > 9) write(x / 10);
putchar((x % 10) | 48);
}
template <typename Int>
inline void write(Int x, char nextch)
{
write(x);
putchar(nextch);
}
int main()
{
return 0;
}
1.2 O(1) int64 乘法
LL mul(LL a, LL b, LL P){
LL L = a * (b >> 25LL) % P * (1LL << 25) % P;
LL R = a * (b & ((1LL << 25) - 1)) % P;
return (L + R) % P;
}
2 图论算法
2.1 树类 - Trie
#define TrieNodeMax 100007
// should @using namespace std
template <int charSetSize = 26, char base = 'a'>
struct Trie {
bool isEndStr[TrieNodeMax];
int son[TrieNodeMax][charSetSize], top = 1;
bool insert(string s)
{
int u = 1;
for (int i = 0; i < s.length(); i++) {
if (!son[u][s[i] - base]) {
son[u][s[i] - base] = ++top;
} else if (isEndStr[son[u][s[i] - base]] || i == s.length() - 1) return 0;
u = son[u][s[i] - base];
if (i == s.length() - 1) isEndStr[u] = 1;
}
return 1;
}
};
2.2 树类 - 并查集(NB version)
复杂度 \(O(n\alpha{(n)})\),不用辅助数组
struct UFS {
int fa[400007];
int familyCnt;
UFS() : familyCnt(0)
{
memset(fa, -1, sizeof(fa));
}
int find(int u)
{
return fa[u] < 0 ? u : fa[u] = find(fa[u]);
}
void connect(int x, int y)
{
int xx = find(x), yy = find(y);
if (xx == yy) return;
if (fa[xx] > fa[yy]) swap(xx, yy);
fa[xx] += fa[yy];
fa[yy] = xx;
familyCnt--;
}
bool isFamily(int u, int v)
{
return find(u) == find(v);
}
} fam;
2.3 树类 - LCA
void dfs_LCA(int now, int depth)
{
use[now] = true;
deep[now] = depth;
for (rg int k = 1; k <= SizeLogN; k++){
f[now][k] = f[f[now][k - 1]][k - 1];
}
for (rg EDGE *nxt = v[now]; nxt; nxt = nxt->next_edge) {
if(!use[nxt->e]) {
f[nxt->e][0] = now;
dfs_LCA(nxt->e, depth + 1);
}
}
// use[now] = false;
}
inline int jump(int u, int depth) {
for (rg int k = 0; k <= SizeLogN; k++) {
if ((depth & (1 << k))) u = f[u][k];
}
return u;
}
inline int LCA(int u, int v){
if (deep[u] < deep[v]) swap(u, v);
u = jump(u, deep[u] - deep[v]);
for (rg int k = SizeLogN; k >= 0; k--) {
if (f[u][k] != f[v][k]) u = f[u][k], v = f[v][k];
}
return u == v ? u : f[u][0];
}
f[s][0] = 0;
dfs_LCA(s, 0);
LCA(u, v);
2.4 树类 - 超级树状数组
\[sum[i]=\sum_{j=1}^ia[j]+\sum_{j=1}^idelta[j]*(i-j+1)
\]
\[sum[i]=\sum_{j=1}^ia[j]+(i+1)*\sum_{j=1}^idelta[j]-\sum_{j=1}^idelta[j]*j
\]
\(basicSum\) 数组维护 \(a\) 数组的前缀和
\(delta1\) 与 \(delta2\) 两个树状数组,\(delta1\) 维护 \(delta\) 数组的和,\(delta2\) 维护\(delta[i]*i\) 的和
template <typename Int>
class super_BIT {
private:
Int n;
Int *basicSum, *delta1, *delta2;
Int lowbit(Int x)
{
return x & (-x);
}
void arr_add(Int *arr, Int pos, Int x)
{
while (pos <= n) {
arr[pos] += x;
pos += lowbit(pos);
}
}
Int arr_getsum(Int *arr, Int pos)
{
Int sum = 0;
while (pos) {
sum += arr[pos];
pos -= lowbit(pos);
}
return sum;
}
void free_space()
{
delete[] basicSum;
delete[] delta1;
delete[] delta2;
}
public:
super_BIT()
{
basicSum = NULL;
delta1 = NULL;
delta2 = NULL;
}
~super_BIT()
{
free_space();
}
void init(Int size, Int *arr = NULL)
{
free_space();
n = size;
basicSum = new Int[size + 1];
delta1 = new Int[size + 1];
delta2 = new Int[size + 1];
memset(basicSum, 0, (size + 1) * sizeof(Int));
memset(delta1, 0, (size + 1) * sizeof(Int));
memset(delta2, 0, (size + 1) * sizeof(Int));
if (arr != NULL) {
for (Int i = 1; i <= n; i++) {
basicSum[i] = basicSum[i - 1] + arr[i];
}
}
}
void modify(Int l, Int r, Int x)
{
arr_add(delta1, l, x);
arr_add(delta1, r + 1, -x);
arr_add(delta2, l, x * l);
arr_add(delta2, r + 1, -x * (r + 1));
}
Int getsum(Int r)
{
return basicSum[r] + arr_getsum(delta1, r) * (r + 1) - arr_getsum(delta2, r);
}
Int query(Int l, Int r)
{
return getsum(r) - getsum(l - 1);
}
void sample(string problem)
{
if (problem == "LOJ - #132") {
Int N, Q;
Int *a = new Int[1000001];
N = read<Int>();
Q = read<Int>();
for (Int i = 1; i <= N; i++) {
a[i] = read<Int>();
}
init(N, a);
Int type, l, r, x;
while (Q--) {
type = read<Int>();
if (type == 1) {
l = read<Int>();
r = read<Int>();
x = read<Int>();
modify(l, r, x);
} else {
l = read<Int>();
r = read<Int>();
write(query(l, r), 10);
}
debug();
}
} else {
// do
}
}
};
2.5 树类 - 平衡树(STL)
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> nums;
int q;
scanf("%d", &q);
while (q--) {
int t, x;
scanf("%d %d", &t, &x);
if (t == 1) {
nums.insert(upper_bound(nums.begin(), nums.end(), x), x);
} else if (t == 2) {
nums.erase(lower_bound(nums.begin(), nums.end(), x));
} else if (t == 3) {
printf("%d\n", lower_bound(nums.begin(), nums.end(), x) - nums.begin() + 1);
} else if (t == 4) {
printf("%d\n", nums[x - 1]);
} else if (t == 5) {
printf("%d\n", *--lower_bound(nums.begin(), nums.end(), x));
} else {
printf("%d\n", *upper_bound(nums.begin(), nums.end(), x));
}
}
return 0;
}
2.6 树类 AC-Trie
2.6.1 模板 1
给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。
n行模式串
1行文本串
struct ACtrie {
ACtrie *fail, *son[charSize];
int count;
bool found;
int fa_sonId; // 在爸爸那里自己的ID,即本节点代表的字母
ACtrie() : fail(NULL), count(0), found(0), fa_sonId('~' - baseChar)
{
memset(son, 0, sizeof(son));
}
};
ACtrie *root = new ACtrie;
void insert(string s)
{
ACtrie *p = root;
for (string::iterator it = s.begin(); it != s.end(); it++) {
if (!p->son[*it - baseChar]) {
p->son[*it - baseChar] = new ACtrie;
p->son[*it - baseChar]->fa_sonId = *it - baseChar;
}
p = p->son[*it - baseChar];
}
p->count++;
}
void make_fail()
{
queue<ACtrie*> q;
for (int i = 0; i < charSize; i++) {
if (root->son[i]) {
// printf("CNT 63 - %c.\n", i + baseChar);
q.push(root->son[i]);
root->son[i]->fail = root;
}
}
while (!q.empty()) {
ACtrie *p = q.front();
q.pop();
for (int i = 0; i < charSize; i++) {
if (!p->son[i]) continue;
ACtrie *f = p->fail;
while (true) {
if (f->son[i]) {
p->son[i]->fail = f->son[i];
break;
}
if (f == root) {
p->son[i]->fail = root;
break;
}
f = f->fail;
}
q.push(p->son[i]);
}
}
}
void build_ACtrie(vector<string> sArr)
{
for (vector<string>::iterator it = sArr.begin(); it != sArr.end(); it++) {
insert(*it);
}
make_fail();
}
int match_task1(string txt) // 有多少个模式串在文本串里出现过(多次出现答案只算1,重复的模式串答案算多个)
{
ACtrie *u = root, *v;
int ans(0);
for (string::iterator it = txt.begin(); it != txt.end(); it++) {
if (u->son[*it - baseChar]) u = u->son[*it - baseChar];
else {
while (!u->son[*it - baseChar] && u != root) {
u = u->fail;
}
if (u->son[*it - baseChar]) {
u = u->son[*it - baseChar];
}
}
v = u;
while (v->count && !v->found) {
ans += v->count;
v->found = true;
v = v->fail;
}
}
return ans;
}
int main()
{
int n = read<int>();
string s, txt;
vector<string> v;
for (int i = 1; i <= n; i++) {
cin >> s;
v.push_back(s);
}
build_ACtrie(v);
cin >> txt;
write(match_task1(txt), 10);
return 0;
}
2.6.2 模板 2
有 N 个由小写字母组成的模式串以及一个文本 T 。每个模式串可能会在文本串中出现多次。你需要找出(b)哪些(/b)模式串在文本串 T 中出现的次数最多。
n行模式串
1行文本串
int TJ[150 + 7];
const int charSize = 26;
const char baseChar = 'a';
struct ACtrie {
ACtrie *fail, *son[charSize];
int count;
string originalString;
bool found;
int inputId;
int fa_sonId; // 在爸爸那里自己的ID,即本节点代表的字母
ACtrie() : fail(NULL), count(0), found(0), fa_sonId('~' - baseChar)
{
memset(son, 0, sizeof(son));
}
};
ACtrie *root = new ACtrie;
void insert(string s, int inputId)
{
ACtrie *p = root;
for (string::iterator it = s.begin(); it != s.end(); it++) {
if (!p->son[*it - baseChar]) {
p->son[*it - baseChar] = new ACtrie;
p->son[*it - baseChar]->fa_sonId = *it - baseChar;
}
p = p->son[*it - baseChar];
}
p->count++;
p->originalString = s;
p->inputId = inputId;
}
void make_fail()
{
queue<ACtrie*> q;
for (int i = 0; i < charSize; i++) {
if (root->son[i]) {
q.push(root->son[i]);
root->son[i]->fail = root;
}
}
while (!q.empty()) {
ACtrie *p = q.front();
q.pop();
for (int i = 0; i < charSize; i++) {
if (!p->son[i]) continue;
ACtrie *f = p->fail;
while (true) {
if (f->son[i]) {
p->son[i]->fail = f->son[i];
// printf("char %c:find my pFail!\n", i + baseChar);
break;
}
if (f == root) {
p->son[i]->fail = root;
break;
}
f = f->fail;
}
q.push(p->son[i]);
}
}
}
void build_ACtrie(vector<string> sArr)
{
int count = 0;
for (vector<string>::iterator it = sArr.begin(); it != sArr.end(); it++) {
insert(*it, count++);
}
make_fail();
}
void match(string txt)
{
ACtrie *u = root, *v;
for (string::iterator it = txt.begin(); it != txt.end(); it++) {
if (u->son[*it - baseChar]) u = u->son[*it - baseChar];
else {
while (!u->son[*it - baseChar] && u != root) {
u = u->fail;
}
if (u->son[*it - baseChar]) {
u = u->son[*it - baseChar];
}
}
v = u;
while (v != root) {
if (v->count) TJ[v->inputId]++;
v = v->fail;
}
}
}
void clean(ACtrie *rt = root)
{
rt->originalString = "";
rt->count = 0;
rt->inputId = 0;
rt->fail = NULL;
for (int i = 0; i < charSize; i++) {
if (rt->son[i]) {
clean(rt->son[i]);
// delete rt->son[i];
}
}
}
struct forSort {
string s;
int id;
int val;
bool operator < (const forSort &other) const {
return val ^ other.val ? val > other.val : id < other.id;;
}
} a[150 + 7];
int main()
{
int n = read<int>();
if (!n) return 0;
sPosTAG:
memset(Jalg::TJ, 0, sizeof(Jalg::TJ));
Jalg::clean();
string s, txt;
vector<string> v;
for (int i = 1; i <= n; i++) {
cin >> s;
v.push_back(s);
}
Jalg::build_ACtrie(v);
cin >> txt;
Jalg::match(txt);
for (int i = 0; i < n; i++) {
a[i] = (forSort){v[i], i, Jalg::TJ[i]};
}
sort(a, a + n);
int ans = a[0].val;
write(ans, 10);
int i = 0;
while (i < n && a[i].val == ans) {
printf("%s\n", a[i++].s.c_str());
}
n = read<int>();
if (n) goto sPosTAG;
return 0;
}
2.7 图类 - 迪杰斯特拉
struct Graph {
struct Node {
int u, val;
bool operator < (const Node &other) const
{
return val > other.val;
}
};
struct Edge {
int v, w;
} e[200007];
int head[100007], next[200007], edgeCnt;
Graph() : edgeCnt(0)
{
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w)
{
e[++edgeCnt] = (Edge){v, w};
next[edgeCnt] = head[u];
head[u] = edgeCnt;
}
void dij(int n, int s, int *dis)
{
memset(dis, 0x3f, sizeof(int) * (n + 1));
dis[s] = 0;
priority_queue<Node> q;
q.push((Node){s, 0});
while (!q.empty()) {
while (q.top().val != dis[q.top().u] && !q.empty()) q.pop();
if (q.empty()) break;
int fr = q.top().u;
q.pop();
#define to e[i].v
#define va e[i].w
for (int i = head[fr]; ~i; i = next[i]) {
if (dis[to] - va > dis[fr]) {
dis[to] = dis[fr] + va;
q.push((Node){to, dis[to]});
}
}
#undef to
#undef va
}
}
} G;
int main()
{
int n, m, s;
int u, v, w;
n = read<int>();
m = read<int>();
s = read<int>();
while (m--) {
u = read<int>();
v = read<int>();
w = read<int>();
G.addEdge(u, v, w);
}
int *dis = new int[n + 1];
G.dij(n, s, dis);
for (int i = 1; i <= n; i++) {
write
(dis[i], 32);
}
return 0;
}
2.8 图类 - SPFA(带卡界)判负环
struct Edge {
int v, w;
int nxt;
Edge() {}
Edge(int _v, int _w, int _nxt) : v(_v), w(_w), nxt(_nxt) {}
} edges[6007];
// 链式前向星存图
int top = 1;
int n, m;
int head[2007] = {0};
int dis[2007] = {0};
bool inqueue[2007] = {0};
inline void add_edge(int u, int v, int w) // 单次加边操作
{
edges[top] = Edge(v, w, head[u]);
head[u] = top++;
}
inline void add(int u, int v, int w) // 加边操作
{
add_edge(u, v, w);
if (w >= 0) add_edge(v, u, w);
}
const double K = 20.076030; // 即题解中所说的 "T"
bool SPFA_bfs()
{
queue <int> q;
q.push(1);
inqueue[1] = 1;
int times = 0;
while (!q.empty()) {
times++;
if (times > K * (n + m)) return 1;
// 以上两行:卡界
int n = q.front(); q.pop();
inqueue[n] = 0;
for (int i = head[n]; i != -1; i = edges[i].nxt) {
Edge &e = edges[i];
if (dis[e.v] > dis[n] + e.w) {
dis[e.v] = dis[n] + e.w;
if (!inqueue[e.v]) q.push(e.v);
}
}
}
return 0;
}
void van()
{
n = readint();
m = readint();
top = 1;
memset(head, -1, sizeof(head));
memset(dis, 0x3f, sizeof(dis));
memset(inqueue, 0, sizeof(inqueue));
dis[1] = 0;
register int ui, vi, wi;
for (register int i = 1; i <= m; i++) {
ui = readint();
vi = readint();
wi = readint();
add(ui, vi, wi);
}
if (SPFA_bfs()) puts("YE5");
else puts("N0");
}
int main()
{
register int T = readint();
while (T--) van();
return 0;
}
2.9 图类 - 网络流
P2045 方格取数加强版
struct Edge {
int u, v, w;
int cost;
int next;
Edge() {}
Edge(int _u, int _v, int _w, int _cost, int _next) : u(_u), v(_v), w(_w), cost(_cost), next(_next) {}
};
int n, k, s, t;
Edge edges[300007];
int head[5007], top = 0;
int could[5007];
int road[5007];
int pre[5007];
template <typename _Tp>
void queue_clear(queue<_Tp> &a)
{
queue<_Tp> empty;
swap(a, empty);
}
queue <int> q;
void _add_once(int u, int v, int w, int cost)
{
edges[top] = Edge(u, v, w, cost, head[u]);
head[u] = top++;
}
void add(int u, int v, int w, int cost)
{
_add_once(u, v, w, cost);
_add_once(v, u, 0, -cost);
}
// 如果你想查看一些 DEBUG ,请去掉下一行开头的注释符号
// #define TEST_OPEN
// 【警告:仅限小数据(如小样例)】
#ifndef TEST_OPEN
#define fprintf(...)
#endif
int dis[10007];
bool SPFA()
{
queue_clear(q);
bool vis[10007] = {0};
memset(dis, 0x3f, sizeof(dis));
memset(could, 0x3f, sizeof(could));
dis[s] = 0;
vis[s] = 1;
q.push(s);
while (!q.empty()) {
int now = q.front();
q.pop();
vis[now] = 0;
for (int nNow = head[now]; nNow != -1; nNow = edges[nNow].next) {
Edge &e = edges[nNow];
if (e.w > 0 && dis[e.v] > dis[now] + e.cost) {
dis[e.v] = dis[now] + e.cost;
road[e.v] = nNow;
could[e.v] = min(could[now], e.w);
pre[e.v] = now;
if (!vis[e.v]) {
q.push(e.v);
vis[e.v] = 1;
}
}
}
}
if (dis[t] != 0x3f3f3f3f) return true; // success
else return false; // unsuccess
}
void EK_MCMF()
{
// EK_MCMF : 基于 EK 的 MCMF 算法
// (Edmonds Karp's "Min Cost Max Flow" algorithm)
int maxflow = 0, mincost = 0;
#ifdef TEST_OPEN
int count = 0;
#endif
while (SPFA()) {
for (int pNow = t; pNow != s; pNow = pre[pNow]) {
edges[road[pNow]].w -= could[t];
edges[road[pNow] ^ 1].w += could[t];
}
maxflow += could[t];
mincost += dis[t] * could[t];
}
printf("%d", -mincost);
#ifdef TEST_OPEN
printf("\nDUBUG:maxflow = %d.\n", maxflow);
#endif
}
enum IN_OUT {
IN = 0,
OUT = 1
};
int getrank(int x, int y, IN_OUT status)
{
return status * n * n + (x - 1) * n + y;
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &k);
s = 0;
t = getrank(n, n, OUT) + 1;
add(s, getrank(1, 1, IN), k, 0);
add(getrank(n, n, OUT), t, k, 0);
int aij;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &aij);
add(getrank(i, j, IN), getrank(i, j, OUT), 1, -aij);
add(getrank(i, j, IN), getrank(i, j, OUT), k - 1, 0);
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < n; j++) {
add(getrank(i, j, OUT), getrank(i, j + 1, IN), k, 0);
}
}
for (int i = 1; i < n; i++) {
for (int j = 1; j <= n; j++) {
add(getrank(i, j, OUT), getrank(i + 1, j, IN), k, 0);
}
}
EK_MCMF();
return 0;
}
3 字符串算法
3.1 KMP
int nxt[1000007];
int n, m;
char a[1000007], b[1000007];
void GetNext()
{
int j = 0, k = -1;
nxt[0] = -1;
while (j < m) {
if (k == -1 || b[j] == b[k]) nxt[++j] = ++k;
else k = nxt[k];
}
}
void Match()
{
int j = -1, k = -1;
while (j < n) {
// printf("j %d k %d.\n", j, k);
if (k == -1 || a[j] == b[k]) j++, k++;
else k = nxt[k];
if (k == m) write(j - m + 1, 10), k = nxt[k];
}
}
int main()
{
scanf("%s%s", a, b);
n = strlen(a);
m = strlen(b);
GetNext();
// for (int i = 0; i < m; i++) nxt[i]++;
Match();
// for (int i = 0; i < m; i++) write(nxt[i] + 1, 32);
for (int i = 1; i <= m; i++) write(nxt[i], 32);
return 0;
}
3.2 manacher
char str[22000007] = {'|', '^'};
int ans[22000007] = {0};
int main()
{
char ch;
int len = 2;
while ((ch = getchar()) != EOF) {
str[len] = ch;
len++;
str[len] = '^';
len++;
}
int mi_2(0), right(0);
int maxLen = 0;
for (int i = 1; i < len; i++) {
ans[i] = right > i ? min(ans[mi_2 - i], right - i) : 1;
while (str[i + ans[i]] == str[i - ans[i]])
ans[i]++;
if (right < i + ans[i]) {
right = i + ans[i];
mi_2 = i << 1;
}
if (maxLen < ans[i])
maxLen = ans[i];
}
write(maxLen - 1);
return 0;
}
4 数论算法
4.1 FFT
version = fast,经典,不是我写的
#define Maxn 1350000
using namespace std;
const double Pi=acos(-1);
int n,m;
struct CP
{
CP (double xx=0,double yy=0){x=xx,y=yy;}
double x,y;
CP operator + (CP const &B) const
{return CP(x+B.x,y+B.y);}
CP operator - (CP const &B) const
{return CP(x-B.x,y-B.y);}
CP operator * (CP const &B) const
{return CP(x*B.x-y*B.y,x*B.y+y*B.x);}
}f[Maxn<<1],p[Maxn<<1];
int tr[Maxn<<1];
void fft(CP *f,bool flag)
{
for (int i=0;i<n;i++)
if (i<tr[i])swap(f[i],f[tr[i]]);
//枚举区间长度
for(int p=2;p<=n;p<<=1){
int len=p>>1;//待合并的长度
CP tG(cos(2*Pi/p),sin(2*Pi/p));
if(!flag)tG.y*=-1;//p次单位根
for(int k=0;k<n;k+=p){//枚举起始点
CP buf(1,0);//遍历一个子问题并合并
for(int l=k;l<k+len;l++){
CP tt=buf*f[len+l];
f[len+l]=f[l]-tt;
f[l]=f[l]+tt;
buf=buf*tG;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=0;i<=n;i++)scanf("%lf",&f[i].x);
for (int i=0;i<=m;i++)scanf("%lf",&p[i].x);
for(m+=n,n=1;n<=m;n<<=1);
for(int i=0;i<n;i++)
tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
fft(f,1);fft(p,1);//DFT
for(int i=0;i<n;++i)f[i]=f[i]*p[i];
fft(f,0);
for(int i=0;i<=m;++i)printf("%d ",(int)(f[i].x/n+0.49));
return 0;
}
version=slow,基础,我写的
struct complex {
double x, y;
complex(double xx = 0, double yy = 0) : x(xx), y(yy) {}
complex operator + (complex b)
{
return complex(x + b.x, y + b.y);
}
complex operator - (complex b)
{
return complex(x - b.x, y - b.y);
}
complex operator * (complex b)
{
return complex(x * b.x - y * b.y, x *b.y + y * b.x);
}
} b[Maxn], c[Maxn];
// struct_complex and operations
int n, m, r[Maxn];
void fft(complex *f, int op)
{
for (int i = 0; i < n; i++) {
if (i < r[i]) {
complex tmp = f[i];
f[i] = f[r[i]];
f[r[i]] = tmp;
}
}
for (int p = 2; p <= n; p <<= 1) {
// $ O(log_n) $
int len = p / 2;
complex tmp(cos(Pi / len), op * sin(Pi / len));
for (register int k = 0; k < n; k += p) {
// $ O(log_n) $
register complex buf(1, 0);
for (register int l = k; l < k + len; l++) {
// $ O(log_n) $
register complex tt = buf * f[len + l];
f[len + l] = f[l] - tt;
f[l] = f[l] + tt;
buf = buf * tmp;
}
}
}
}
int main()
{
n = read<int>();
m = read<int>();
for (register int i = 0; i <= n; i++) {
b[i].x = read<int>();
}
for (register int i = 0; i <= m; i++) {
c[i].x = read<int>();
}
m += n;
n = 1;
while (n <= m) {
n <<= 1;
}
for (int i = 0; i < n; i++) {
r[i] = (r[i >> 1] >> 1) | ((i & 1) ? n >> 1 : 0);
}
fft(b, 1);
fft(c, 1);
// DFT
for (register int i = 0; i < n; i++) b[i] = b[i] * c[i];
fft(b, -1);
// IDFT
for (register int i = 0; i <= m; i++) {
printf("%.0f ", fabs(b[i].x) / n);
}
return 0;
}