A Theramore

题意:给定一个长度为 nnn 的 010101 串,每次可以选取一个奇数长度的连续子串进行位置上的翻转,问经过若干次操作能得到的字典序最小的串。n≤5×105n \leq 5\times 10^5n≤5×105。

解法:不难发现,处于奇数位置的数字永远在奇数位,处于偶数位的数字永远在偶数位,并且可以通过长度为 333 的连续子串不断翻转使得只有奇数或偶数位的数字在翻转。因而等效于奇数位置和偶数位置单独排序。

B Darkmoon Faire

题意:给定一个互不相等的长度为 nnn 的序列 {ai}\{a_i\}{ai​},将其划分为若干段并将每段都视为 111-base 的数组 ,使得每一段的最大值处于奇数位,每一段的最小值处于偶数位,问划分方案数。n≤3×105n \leq 3\times 10^5n≤3×105。

解法:考虑 fif_ifi​ 表示前 iii 个数字的划分方法,则 fif_ifi​ 可以从所有满足上述条件的子段 [j+1,i][j+1,i][j+1,i] 中转移。考虑如何快速找到这些 jjj。若插入 aia_iai​ 后,当前的最大值所处位置为 kkk,则仅考虑最大值条件,与 kkk 同奇偶的 jjj 即可成功转移;对于最小值则是不同奇偶。因而需要支持只对奇数位置激活和只对偶数位置激活,查询同时满足两个条件的位置的和的数据结构。

利用单调栈来维护包含第 iii 个数字的连续子段中最大值和最小值的位置,同时维护两个线段树分别表示奇数位置和偶数位置的 dp 值。标记分为最大值标记和最小值标记,查询的时候仅查询同时有两个标记的点。注意利用单调栈的性质,一个位置不会同时被多个最大值标记同时覆盖,因而可以直接简单相加所有的标记的和来表示标记种类。总时间复杂度 O(nlog⁡n)\mathcal O(n \log n)O(nlogn)。

#include <bits/stdc++.h>
using namespace std;
const long long mod = 998244353;
const int N = 300000;
class segment_tree
{struct node{long long sum, addtag;int tag;node(){sum = addtag = 0;tag = 0;}};int n;node t[4 * N + 5];void build(int place, int left, int right){t[place].sum = t[place].addtag = t[place].tag = 0;if (left == right)return;int mid = (left + right) >> 1;build(place << 1, left, mid);build(place << 1 | 1, mid + 1, right);}void pushup(int place){t[place].tag = max(t[place << 1].tag, t[place << 1 | 1].tag);t[place].sum = 0;if (t[place].tag == t[place << 1].tag)t[place].sum = (t[place].sum + t[place << 1].sum) % mod;if (t[place].tag == t[place << 1 | 1].tag)t[place].sum = (t[place].sum + t[place << 1 | 1].sum) % mod;}void pushdown(int place, int left, int right){if (t[place].addtag){int mid = (left + right) >> 1;update(place << 1, left, mid, left, mid, t[place].addtag);update(place << 1 | 1, mid + 1, right, mid + 1, right, t[place].addtag);t[place].addtag = 0;}}void update(int place, int left, int right, int start, int end, int x){if (start <= left && right <= end){t[place].tag += x;t[place].addtag += x;return;}pushdown(place, left, right);int mid = (left + right) >> 1;if (start <= mid)update(place << 1, left, mid, start, end, x);if (end > mid)update(place << 1 | 1, mid + 1, right, start, end, x);pushup(place);}void update(int place, int left, int right, int start, long long x){if (left == right){t[place].sum = x;return;}pushdown(place, left, right);int mid = (left + right) >> 1;if (start <= mid)update(place << 1, left, mid, start, x);elseupdate(place << 1 | 1, mid + 1, right, start, x);pushup(place);}public:void build(int n){this->n = n;build(1, 1, n);}void update(int l, int r, int x){update(1, 1, n, l, r, x);}void update(int pos, long long x){update(1, 1, n, pos, x);}long long query(){if (t[1].tag == 2)return t[1].sum;elsereturn 0;}
} T[2];
int st[2][N + 5], tp[2], a[N + 5];
long long f[N + 5];
int main()
{int t, n;scanf("%d", &t);while(t--){scanf("%d", &n);T[0].build(n);T[1].build(n);for (int i = 1; i <= n;i++)scanf("%d", &a[i]);f[0] = 1;for (int i = 1; i <= n; i++){T[i & 1].update(i, f[i - 1]);while (tp[0] && a[i] > a[st[0][tp[0]]]){T[st[0][tp[0]] & 1].update(st[0][tp[0] - 1] + 1, st[0][tp[0]], -1);tp[0]--;}T[i & 1].update(st[0][tp[0]] + 1, i, 1);st[0][++tp[0]] = i;while (tp[1] && a[i] < a[st[1][tp[1]]]){T[(st[1][tp[1]] & 1) ^ 1].update(st[1][tp[1] - 1] + 1, st[1][tp[1]], -1);tp[1]--;}T[(i & 1) ^ 1].update(st[1][tp[1]] + 1, i, 1);st[1][++tp[1]] = i;f[i] = (T[0].query() + T[1].query()) % mod;}printf("%lld\n", f[n]);tp[0] = tp[1] = 0;}return 0;
}

D Quel’Thalas

题意:问最少需要画多少条不过原点的直线,使得可以经过 {(i,j)∣i,j∈[0,n]}/(0,0)\{(i,j)|i,j \in [0,n] \}/(0,0){(i,j)∣i,j∈[0,n]}/(0,0)。

解法:作 2n2n2n 条 x+y=bx+y=bx+y=b 的直线即可。故答案为 2n2n2n。

E Ironforge

题意:给定长度为 nnn 的序列 {ai}\{a_i\}{ai​} 和 n−1n-1n−1 条边,第 iii 条边连接 (i,i+1)(i,i+1)(i,i+1)。每条边上有个质数 pip_ipi​,能通过它当且仅当已经走过的位置中的数字中有 pip_ipi​ 的倍数。qqq 次询问从 xxx 号节点出发能不能到达 yyy 号节点。n,q≤2×105n,q \leq 2\times 10^5n,q≤2×105。

解法:显然需要求出每个点能走到的范围,然后 O(1)\mathcal O(1)O(1) 查询。

如果暴力计算,则可以每次从一个点开始向两侧扩展。对于一条边是否可以经过,可以提前 O(n)\mathcal O(n)O(n) 的预处理其左右两侧最近是它倍数的数字位置,以实现 O(1)\mathcal O(1)O(1) 的查询。这样暴力的复杂度为 O(n2)\mathcal O(n^2)O(n2)。

考虑如何加速上述过程。首先当扩展到一个新点,可以将它所能扩展到的区域直接拿过来。此外,当当前节点 iii 要进行左侧扩展时,对于扩展到的新点 i−1i-1i−1 进行继续扩展,其搜索范围也仅限于点 iii 的左侧,不会回头。向右扩展同理。具体细节参看下方代码。

(复杂度非常的玄学,不便于分析)。

#include <bits/stdc++.h>
using namespace std;
const int N = 200000;
bool vis[N + 5];
int prime[N + 5], tot;
vector<int> factor[N + 5];
void sieve(int n)
{for (int i = 2; i <= n;i++){if(!vis[i]){prime[++tot] = i;factor[i] = {i};}for (int j = 1; j <= tot && (long long)prime[j] * i <= n;j++){int num = prime[j] * i;vis[num] = 1;factor[num] = factor[i];if (i % prime[j])factor[num].push_back(prime[j]);elsebreak;}}
}
int main()
{sieve(N);int t, n, q, x, y;scanf("%d", &t);while(t--){scanf("%d%d", &n, &q);vector<int> llim(n), rlim(n), lbound(n), rbound(n);vector<int> a(n), b(n - 1);for (int i = 0; i < n;i++)scanf("%d", &a[i]);for (int i = 0; i < n - 1;i++)scanf("%d", &b[i]);vector<int> pos(N, -1);for (int i = 0; i < n - 1;i++){for (auto j : factor[a[i]])pos[j] = i;llim[i] = pos[b[i]];}for (auto &i : pos)i = n + 1;for (int i = n - 1; i >= 1;i--){for (auto j : factor[a[i]])pos[j] = i;rlim[i - 1] = pos[b[i - 1]];}for (int i = 0; i < n;i++)lbound[i] = rbound[i] = i;function<void(int, int, int)> dfs = [&](int pos, int left, int right){if (left > right)return;bool flag = 1;while (flag){flag = 0;if (lbound[pos] > left && rlim[lbound[pos] - 1] <= rbound[pos]){flag = 1;int now = --lbound[pos];dfs(now, 0, left - 1);lbound[pos] = min(lbound[pos], lbound[now]);rbound[pos] = max(rbound[pos], rbound[now]);}if (rbound[pos] < right && llim[rbound[pos]] >= lbound[pos]){flag = 1;int now = ++rbound[pos];dfs(now, right + 1, n - 1);lbound[pos] = min(lbound[pos], lbound[now]);rbound[pos] = max(rbound[pos], rbound[now]);}}};for (int i = 0; i < n;i++)dfs(i, 0, n - 1);while (q--){scanf("%d%d", &x, &y);x--;y--;if (lbound[x] <= y && y <= rbound[x])printf("Yes\n");elseprintf("No\n");}}return 0;
}

G Darnassus

题意:给定一个长度为 nnn 的排列 {pi}\{p_i\}{pi​},任意两个点 (i,j)(i,j)(i,j) 的代价为之间有一条代价为 ∣i−j∣(pi−pj)|i-j|(p_i-p_j)∣i−j∣(pi​−pj​) 的边,问这 nnn 个点的最小生成树权值。n≤5×104n\leq 5\times 10^4n≤5×104。

解法:由 Kruskal 可以得到,显然可以通过相邻连接使得图完全连通。因而最大边的边权不会超过 nnn,因而 ∣i−j∣|i-j|∣i−j∣ 与 ∣pi−pj∣|p_i-p_j|∣pi​−pj​∣ 中必然有一个是小于等于 n\sqrt nn​ 的。暴力枚举 ∣i−j∣∈[1,n]|i-j| \in [1, \sqrt n]∣i−j∣∈[1,n​] 和 ∣pi−pj∣∈[1,n]|p_i-p_j| \in [1,\sqrt n]∣pi​−pj​∣∈[1,n​] 即可。对边使用桶排序,总时间复杂度 O(nn)\mathcal O(n \sqrt n)O(nn​)。

#include <bits/stdc++.h>
using namespace std;
const int N = 50000;
int pos[N + 5], a[N + 5];
vector<pair<int, int>> que[N + 5];
int father[N + 5];
int getfather(int x)
{return father[x] == x ? x : father[x] = getfather(father[x]);
}
int main()
{int t, n;scanf("%d", &t);while(t--){scanf("%d", &n);for (int i = 1; i <= n;i++)father[i] = i;int m = sqrt(n) + 1;for (int i = 1; i <= n;i++){scanf("%d", &a[i]);pos[a[i]] = i;}long long ans = 0;for (int i = 1; i <= m; i++)for (int j = 1; i + j <= n;j++){int val = i * abs(a[i + j] - a[j]);if(val < n)que[val].emplace_back(i + j, j);}for (int i = 1; i <= m; i++)for (int j = 1; i + j <= n;j++){int val = i * abs(pos[i + j] - pos[j]);if(val < n)que[val].emplace_back(pos[i + j], pos[j]);}int add = 0;for (int i = 1; i <= n && add < n - 1; i++)for (auto j : que[i])if (getfather(j.first) != getfather(j.second)){father[getfather(j.first)] = getfather(j.second);ans += i;add++;if(add == n - 1)break;}printf("%lld\n", ans);for (int i = 1; i <= n; i++)que[i].clear();}return 0;
}

H Orgrimmar

题意:给定一个 nnn 个点的树,从中选出一些点,使得其导出子图中每个点的度不超过 111,问最多可以选择几个点。n≤5×105n \leq 5\times 10^5n≤5×105。

解法:考虑树形 dp。fi,0/1/2f_{i,0/1/2}fi,0/1/2​ 表示当前 iii 节点不选、选了且度为 000、选了且度为 111 的子树 iii 最大选点数目。则有如下转移:
fu,0←∑v∈chumax⁡(fv,0,fv,1,fv,2)fu,1←1+∑v∈chufv,0fu,2←1+max⁡v′{fv′,1+∑v∈chu,v≠v′fv,0}\begin{aligned} f_{u,0} &\leftarrow \sum_{v \in {\rm ch}_u}\max(f_{v,0},f_{v,1},f_{v,2})\\ f_{u,1} &\leftarrow 1+\sum_{v \in {\rm ch}_u}f_{v,0}\\ f_{u,2} &\leftarrow 1+\max_{v'}\left\{f_{v',1}+\sum_{v \in {\rm ch}_u,v \neq v'}f_{v,0}\right\}\\ \end{aligned} fu,0​fu,1​fu,2​​←v∈chu​∑​max(fv,0​,fv,1​,fv,2​)←1+v∈chu​∑​fv,0​←1+v′max​⎩⎨⎧​fv′,1​+v∈chu​,v=v′∑​fv,0​⎭⎬⎫​​
若当前节点不选,则子树可以乱选;若当前节点选,且度为 000(即不和子树连接),则子树只能选择都不选;否则就可以选择一个子树去连接,其余的均不选。总时间复杂度 O(n)\mathcal O(n)O(n)。

#include <bits/stdc++.h>
using namespace std;
const int N = 500000;
struct line
{int from;int to;int next;
};
struct line que[2 * N + 5];
int cnt, headers[N + 5];
void add(int from, int to)
{cnt++;que[cnt].from = from;que[cnt].to = to;que[cnt].next = headers[from];headers[from] = cnt;
}
int f[N + 5][3];
void dfs(int place, int father)
{f[place][0] = 0;f[place][1] = f[place][2] = 1;for (int i = headers[place]; i; i = que[i].next)if (que[i].to != father){dfs(que[i].to, place);f[place][0] += max({f[que[i].to][0], f[que[i].to][1], f[que[i].to][2]});f[place][1] += f[que[i].to][0];}for (int i = headers[place]; i; i = que[i].next)if (que[i].to != father)f[place][2] = max(f[place][2], f[place][1] - f[que[i].to][0] + f[que[i].to][1]);
}
int main()
{int size(512 << 20); // 512M__asm__ ( "movq %0, %%rsp\n"::"r"((char*)malloc(size)+size));int t, n;scanf("%d", &t);while(t--){scanf("%d", &n);for (int i = 1, u, v; i < n;i++){scanf("%d%d", &u, &v);add(u, v);add(v, u);}dfs(1, 1);printf("%d\n", max({f[1][0], f[1][1], f[1][2]}));cnt = 0;for (int i = 1; i <= n;i++)headers[i] = 0;}exit(0);
}

I Gilneas

题意:给定一个 nnn 个点,以 111 为根的树,按照顺序依次进行 qqq 次操作:从某个点 xix_ixi​ 开始,一路走到 111,将其路径上所有的边的颜色全部染成 cic_ici​,并且与这条路径相连的其余所有的边的颜色全部清零。每个操作有 pip_ipi​ 的概率成功,彼此独立,问最终所有边上颜色之和的期望。n,q≤2×105n, q\leq 2\times 10^5n,q≤2×105。

解法:考虑每条边对答案的贡献。从后到前的维护每个操作,考虑一条边是否会被最终留下来:

对于边 (u,v)(u,v)(u,v),它如果想要留下来,当且仅当需要经过 u→wu \to wu→w 的染色全部失败——从 v1→uv_1 \to uv1​→u 的必然经过 u→wu \to wu→w;从 s1→vs_1 \to vs1​→v 再上去的也得经过 u→wu \to wu→w;从 uuu 出发的 u→wu \to wu→w 也是经过 u→wu \to wu→w。因而每条边是否取到仅限制于其父亲往根节点走的那条边。若 uuu 为根节点,则 (u,v)(u,v)(u,v) 的颜色为 cic_ici​ 当且仅当后面所有的染色全部失败。

显然一次操作对应的若干条边的贡献可以通过一次树链剖分一并统计。考虑每条边用它的儿子来代表,对于根节点也建立一条虚边指向虚点 000。则问题转化为链上求和、链上乘法,使用树链剖分则可以在 O(nlog⁡2n)\mathcal O(n \log^2n)O(nlog2n) 的复杂度内通过。

#include <bits/stdc++.h>
using namespace std;
const int N = 200000;
const long long mod = 1000000007;
long long power(long long a, long long x)
{long long ans = 1;while(x){if(x&1)ans = ans * a % mod;a = a * a % mod;x >>= 1;}return ans;
}
long long inv(long long a)
{return power(a, mod - 2);
}
class segment_tree
{struct node{long long sum;long long tag;node(){tag = 1;sum = 0;}};int n;node t[4 * N + 5];void pushdown(int place, int left, int right){if (t[place].tag != 1){int mid = (left + right) >> 1;update(place << 1, left, mid, left, mid, t[place].tag);update(place << 1 | 1, mid + 1, right, mid + 1, right, t[place].tag);t[place].tag = 1;}}void update(int place, int left, int right, int start, int end, long long x){if (start <= left && right <= end){t[place].tag = t[place].tag * x % mod;t[place].sum = t[place].sum * x % mod;return;}pushdown(place, left, right);int mid = (left + right) >> 1;if (start <= mid)update(place << 1, left, mid, start, end, x);if (end > mid)update(place << 1 | 1, mid + 1, right, start, end, x);t[place].sum = (t[place << 1].sum + t[place << 1 | 1].sum) % mod;}long long query(int place, int left, int right, int start, int end){if (start <= left && right <= end)return t[place].sum;pushdown(place, left, right);int mid = (left + right) >> 1;long long ans = 0;if (start <= mid)ans = (ans + query(place << 1, left, mid, start, end));if (end > mid)ans = (ans + query(place << 1 | 1, mid + 1, right, start, end));return ans;}void build(int place, int left, int right){t[place].tag = 1;t[place].sum = 1;if (left == right)return;int mid = (left + right) >> 1;build(place << 1, left, mid);build(place << 1 | 1, mid + 1, right);t[place].sum = (t[place << 1].sum + t[place << 1 | 1].sum) % mod;}public:void build(int n){this->n = n;build(1, 1, n);}void update(int left, int right, long long x){update(1, 1, n, left, right, x);}long long query(int left, int right){return query(1, 1, n, left, right);}
} t;
struct line
{int from;int to;int next;
};
struct line que[2 * N + 5];
int cnt, headers[N + 5];
void add(int from, int to)
{cnt++;que[cnt].from = from;que[cnt].to = to;que[cnt].next = headers[from];headers[from] = cnt;
}
int father[N + 5], siz[N + 5], maxson[N + 5];
void dfs1(int place, int fa)
{father[place] = fa;siz[place] = 1;maxson[place] = 0;for (int i = headers[place]; i; i = que[i].next)if (que[i].to != fa){dfs1(que[i].to, place);siz[place] += siz[que[i].to];if(!maxson[place] || siz[maxson[place]] < siz[que[i].to])maxson[place] = que[i].to;}
}
int tp[N + 5], id[N + 5], ind;
void dfs2(int place, int ance)
{tp[place] = ance;id[place] = ++ind;if(maxson[place])dfs2(maxson[place], ance);for (int i = headers[place]; i; i = que[i].next)if (que[i].to != maxson[place] && que[i].to != father[place])dfs2(que[i].to, que[i].to);
}
void update(int u, long long p)
{while (u){t.update(id[tp[u]], id[u], p);u = father[tp[u]];}
}
long long query(int u)
{if(u == 1)return 0;u = father[u];long long ans = 0;while (u){ans = (ans + t.query(id[tp[u]], id[u])) % mod;u = father[tp[u]];}return ans;
}
struct operation
{int id, c;long long p;
} op[N + 5];
int main()
{int caset, n, q;scanf("%d", &caset);while(caset--){scanf("%d%d", &n, &q);for (int i = 2, u; i <= n;i++){scanf("%d", &u);add(i, u);add(u, i);}dfs1(1, 0);dfs2(1, 0);t.build(ind);for (int i = 1; i <= q;i++)scanf("%d%d%lld", &op[i].id, &op[i].c, &op[i].p);long long ans = 0;for (int i = q; i >= 1;i--){ans = (ans + op[i].p * query(op[i].id) % mod * op[i].c % mod) % mod;update(op[i].id, mod + 1 - op[i].p);}printf("%lld\n", ans);ind = cnt = 0;for (int i = 1; i <= n;i++)headers[i] = 0;}return 0;
}

J Vale of Eternal

题意:平面上初始有 nnn 个点 (xi,yi)(x_i,y_i)(xi​,yi​)。ttt 时刻平面上初始的 nnn 个点会变成 4n4n4n 个点——(xi+t,yi)(x_i+t,y_i)(xi​+t,yi​),(xi−t,yi)(x_i-t,y_i)(xi​−t,yi​),(xi,yi−t)(x_i,y_i-t)(xi​,yi​−t),(xi,yi+t)(x_i,y_i+t)(xi​,yi​+t)。qqq 次询问 ttt 时刻这 4n4n4n 个点围成的凸包面积。n,q≤1×105n,q \leq 1\times 10^5n,q≤1×105,坐标范围 1×1091\times 10^91×109。

解法:首先对这 nnn 个点取凸包 CmC_mCm​。考虑 ttt 时刻的凸包:

容易发现周围一定有一个完整的边长为 2t\sqrt 2t2​t 的正方形,并且每条边向外扩展了 ttt。对于一条边 ABABAB,若斜率的绝对值超过 111,则其扩展面积为 ∣yA−yB∣t|y_A-y_B|t∣yA​−yB​∣t;否则为 ∣xA−xB∣t|x_A-x_B|t∣xA​−xB​∣t。因而扩展面积为 t∑i=1mmax⁡(∣xi−xi+1∣,∣yi−yi+1∣)\displaystyle t\sum_{i=1}^m \max(|x_i-x_{i+1}|,|y_i-y_{i+1}|)ti=1∑m​max(∣xi​−xi+1​∣,∣yi​−yi+1​∣)。因而 ttt 时刻的答案为 2t2+t∑i=1mmax⁡(∣xi−xi+1∣,∣yi−yi+1∣)+S\displaystyle 2t^2+t\sum_{i=1}^m \max(|x_i-x_{i+1}|,|y_i-y_{i+1}|)+S2t2+ti=1∑m​max(∣xi​−xi+1​∣,∣yi​−yi+1​∣)+S,其中 SSS 为初始面积。由于给定均为整点,因而需要使用整型,并且最后手动处理小数点(至多只有一位小数)。

int main()
{int caset, n, q;long long t;scanf("%d", &caset);while(caset--){scanf("%d%d", &n, &q);vector<Point> que(n);for (int i = 0; i < n;i++)scanf("%lld%lld", &que[i].x, &que[i].y);Convex c = convexhull(que);long long doubleS = c.area();long long C = 0;int n = c.p.size();auto nx = [&](int x){return (x + 1) % n;};for (int i = 0; i < n; i++)C += max(abs(c.p[i].x - c.p[nx(i)].x), abs(c.p[i].y - c.p[nx(i)].y));while (q--){scanf("%lld", &t);long long ans = t * C + 2 * t * t + doubleS / 2;printf("%lld", ans);if(doubleS & 1)printf(".5\n");elseprintf(".0\n");}}return 0;
}

K Stormwind

题意:给定 n×mn \times mn×m 的矩形,横平竖直的切分尽可能多刀使得每块子矩形的面积均大于等于 kkk,问最多切多少刀。n,m,k≤1×105n,m,k \leq 1\times 10^5n,m,k≤1×105。

解法:枚举子矩形的长 xxx,求出最小的子矩形的宽 y=⌈kx⌉y=\left \lceil \dfrac{k}{x}\right \rceily=⌈xk​⌉,求 max⁡1≤x≤k{⌊(nx⌋+⌊my⌋}−2\displaystyle \max_{1 \leq x\leq k}\left \{\left \lfloor \dfrac{\mathstrut n}{x}\right \rfloor+\left \lfloor \dfrac{m}{y}\right \rfloor\right \}-21≤x≤kmax​{⌊x(n​⌋+⌊ym​⌋}−2,必须保证 x≤nx \leq nx≤n 且 y≤my \leq my≤m。时间复杂度 O(k)\mathcal O(k)O(k)。

M Shattrath City

题意:求长度为 mmm,序列中每个元素属于 [1,n][1,n][1,n],且任意连续 nnn 个数字不能是长度为 nnn 的排列的序列个数。n,m≤2×105n,m \leq 2\times 10^5n,m≤2×105。

解法:考虑 fif_ifi​ 表示在 iii 处开头出现一个长度为 nnn 的排列,且前 i−1i-1i−1 个数字构不成一个排列的方案数。有转移:
fi=ni−1n!−∑j=1i−nfj(ni−j−nn!)−∑j=i−n+1i−1fj(i−j)!f_i=n^{i-1}n!-\sum_{j=1}^{i-n}f_j(n^{i-j-n}n!)-\sum_{j=i-n+1}^{i-1}f_j(i-j)! fi​=ni−1n!−j=1∑i−n​fj​(ni−j−nn!)−j=i−n+1∑i−1​fj​(i−j)!
即,全集 ni−1n!n^{i-1}n!ni−1n! 减去 [1,i−1][1,i-1][1,i−1] 包含一个完整排列的方案,减去上一个排列和当前排列有交集的方案。则很容易的利用分治 NTT 优化到 O(nlog⁡2n)\mathcal O(n \log^2n)O(nlog2n),在实际测评中其效率与多项式求逆的效率差不多。

void Solve() {scanf("%d%d", &n, &m);int w = POW(n, m - n, fac[n]);Poly g(m + 1);g[1] = POW(n);fp(i, 2, n) g[i] = mul(g[i - 1], g[1]);fp(i, 2, n) g[i] = mul(g[i], fac[i]);fp(i, n + 1, m) g[i] = g[n];Poly h = (g >> 1).semiConv({w}, m - n + 1, [&](Poly &f, int m) { f[m] = (w - f[m] + P) % P; });ll ans = POW(n, m);fp(i, 0, m - n) ans += P - h[i];printf("%lld\n", ans % P);
}

2022 年杭电多校第八场补题记录相关推荐

  1. 2021杭电多校第八场补题

    比赛传送门:Contest Problem List (hdu.edu.cn) 1006)GCD Game 题目翻译:爱丽丝和鲍勃正在玩游戏. 他们轮流操作.有n个数字,a1,a2,...,an.每次 ...

  2. 2022 年牛客多校第四场补题记录

    A Task Computing 题意:给定长度为 nnn 的序列 {(wi,pi)}\{(w_i,p_i)\}{(wi​,pi​)},从中选出 mmm 项并重新排列得到子序列 {a1,a2,⋯,am ...

  3. 2022年牛客多校第三场补题记录

    A Ancestor 题意:给出两棵 nnn 个节点的树 A,BA,BA,B,A,BA,BA,B 树上每个节点均有一个权值,给出 kkk 个关键点的编号 x1,x2,⋯,xkx_1, x_2, \cd ...

  4. 2022杭电多校第八场题解

    2022杭电多校第八场 Theramore(思维) 题意 给定一个01字符串,每次可以将一个奇数长度的区间翻转,求操作后字典序最小的字符串. 分析 翻转奇数长度的区间,元素位置的奇偶性不变,统计奇数位 ...

  5. 2022杭电多校第八场

    文章目录 1001.Theramore 1007.Darnassus 1008.Orgrimmar 1011.Stormwind 1001.Theramore 易知,字符串的奇偶项之间不会互换,只需要 ...

  6. hdu 6656 2019杭电多校第7场 期望题

    设f[i]为从i升级到i+1期望需要的金钱,由于每级都是能倒退或者升级到i+1,所以询问从l,r的期望金钱可以直接前缀和,那么推导每一级升级需要的期望钱也可以用前缀和推导 设sum[i]=f[1]+f ...

  7. 2019暑假杭电多校第6场签到题-1008-TDL

    题目传送门 思路: 估计出n的范围,暴力就完事. 异或就是不进位的加法 (f(n,m) - n)^n == k, f(n,m)-n==k^n; 因为灯饰右边估计不会超过1e3,所以k^n<=1e ...

  8. 2021牛客多校第八场补题 D-OR

    链接:https://ac.nowcoder.com/acm/contest/11259/D 来源:牛客网 题目描述 There are two sequences of length n−1n-1n ...

  9. 杭电多校第六场个人补题6 7 9 10 12

    杭电多校第六场个人补题6 7 9 10 12 6 题意 给定一棵有n结点的树,点权为1~n,求对所有结点子树的mex操作和的最大值 思路 其实就是从最底部开始网上找,由于0是唯一的一个,所欲最好给在最 ...

最新文章

  1. vue实战之前期准备
  2. [云炬创业基础笔记]第六章商业模式测试6
  3. 18 | 案例篇:内存泄漏了,我该如何定位和处理?
  4. 复制Java文件打印流改进版
  5. 现在也是只能谢谢随笔了,但是在以后收货的日子里会有更多的感想记下
  6. 2021年中国企业直播研究及服务商品牌测评报告
  7. gitolite安装及配置教程centos7
  8. 二维前缀和(附模板题)
  9. Grafana实现zabbix数据可视化展示
  10. Linux下update和upgrade的区别
  11. 新手小白之学习python一飞冲天日志之—基本数据类型,条件控制语句
  12. 几个关于oracle 11g ASM的问题
  13. 2021-06-25绝对定位的理解
  14. Max Script|多边形的控制
  15. 便携式频谱仪无人机机载频谱仪解决方案
  16. 关于Windows API、CRT和STL二三事
  17. impress.js css模板,使用impress.js制作幻灯片
  18. angular *ngFor
  19. 【FFH】如何在鸿蒙系统上进行抓包测试
  20. 黄金发展期已远,蜻蜓、荔枝、喜马拉雅们下一个增长点何在?

热门文章

  1. 计算机单片机实训报告,计算器单片机实训报告.doc
  2. 聚英国际|【燎原计划启动周第二期AMA】Filecoin经济模型及热点争议解读
  3. 一文读懂:股权激励的“三重境界”
  4. 本源量子与德美牵头成立产业联盟,生物化学正式进入量子计算“赛道”|现场专访
  5. linux cut命令
  6. 一个汽车软件测试工程师的三年工作总结
  7. 《汇编语言》第十章 call 和 ret 指令
  8. bitset的使用示例
  9. HDU-4069___Squiggly Sudoku —— 锯齿数独 + BFS
  10. Maven:命令大全。