Tag

计数+LIS, 二分+ST表, 计数+记搜

A. 改造二叉树

Description

题面

Solution

如果目标序列非严格递增,或者说目标序列是不下降的,那么答案就是 \(n\) 减去最长不下降子序列的长度。

比如这种情况:\(2\ 3\ 1\ 4\),\(LIS\) 为 \(2\ 3\ 4\),答案求出来为 \(1\),但由于整数的限制,应该要修改 \(2\) 次。即直接 \(LIS\) 求出的答案是在非严格递增的情况下的答案。

现在要求目标序列严格递增,一个常见的将严格递增整数序列映射成非严格递增整数序列的技巧就是将如下序列:

\(a_1, a_2, a_3, a_4 ... a_n\)

映射成:

\(a_1 - 1, a_2 - 2, a_3 - 3, a_4 - 4 ... a_n - n\)

这种方法常见于计数类问题。

这样映射后求最长不下降子序列的长度就没问题了。

考虑证明这个做法的正确性,如果 \(O(n^2)\) 转移 \(LIS\) 的话,每次从位置 \(i\) 转移到位置 \(j\) 时,只有满足 \(a[j]-a[i] \geq j-i\) 才能转移,而整体减去 \(\{1, 2, 3, ..., n\}\) 正是对这个限制的体现。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std; const int N = 1e5 + 3;
int n, fa, d, sum, qr, l, r, mid, top, stk[N], f[N], a[N], b[N], lc[N], rc[N];
bool vis[N]; char ch;
int read() {
while (ch = getchar(), ch < '0' || ch > '9');
int res = ch - 48;
while (ch = getchar(), ch >= '0' && ch <= '9') res = res * 10 + ch - 48;
return res;
} void Bfs() {
int x; stk[top = 1] = 1;
while (top) {
x = stk[top];
if (lc[x] && !vis[lc[x]]) {
stk[++top] = lc[x];
continue;
}
b[++sum] = a[x]; b[sum] -= sum;
vis[x] = true; --top;
if (rc[x] && !vis[rc[x]]) {
stk[++top] = rc[x];
continue;
}
}
return ;
} int main() {
freopen("binary.in", "r", stdin);
freopen("binary.out", "w", stdout);
n = read();
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = 2; i <= n; ++i) {
fa = read(); d = read();
(d ? rc[fa] : lc[fa]) = i;
}
Bfs();
f[qr = 1] = b[1];
for (int i = 2; i <= n; ++i) {
if (b[i] >= f[qr]) f[++qr] = b[i];
else {
l = 1; r = qr;
while (l <= r) {
mid = l + r >> 1;
if (f[mid] <= b[i]) l = mid + 1;
else r = mid - 1;
}
f[l] = b[i];
}
}
cout << n - qr << endl;
fclose(stdin); fclose(stdout);
return 0;
}

B. 数字对

Description

题面

Solution 1

二分区间长度,枚举左端点,显然可能的 \(a_k\) 就是区间最小值,判断 \(a_k\) 是否能整除这个区间的所有数就是判断 \(a_k\) 是否与这个区间的 \(gcd\) 相等,\(ST\)表维护区间最小值和区间\(gcd\)。

#include <cstdio>
#include <cmath> const int N = 500005;
int f[N][21], g[N][21], ans[N], n; //区间最小值,区间gcd int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
int min(int x, int y) {
return x < y ? x : y;
}
int gcd(int a, int b) {
return !b ? a : gcd(b, a % b);
}
int Min(int l, int r) {
int k = log2(r - l + 1);
return min(f[l][k], f[r-(1<<k)+1][k]);
}
int Gcd(int l, int r) {
int k = log2(r - l + 1);
return gcd(g[l][k], g[r-(1<<k)+1][k]);
}
bool check(int k) {
int sta[N] = {}, top = 0;
for (int i = 1; i + k - 1 <= n; ++i)
if (Min(i, i + k - 1) == Gcd(i, i + k - 1))
sta[++top] = i;
if (!top) return false;
ans[0] = top;
for (int i = 1; i <= top; ++i) ans[i] = sta[i];
return true;
} int main() {
freopen("pair.in", "r", stdin);
freopen("pair.out", "w", stdout); n = read();
for (int i = 1; i <= n; ++i) f[i][0] = g[i][0] = read();
for (int j = 1; j <= 19; ++j)
for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
f[i][j] = min(f[i][j-1], f[i+(1<<j-1)][j-1]);
g[i][j] = gcd(g[i][j-1], g[i+(1<<j-1)][j-1]);
}
int l = 0, r = n;
while (l < r) { //二分区间长度
int mid = l + (r - l + 1 >> 1);
if (check(mid)) l = mid;
else r = mid - 1;
}
printf("%d %d\n", ans[0], l - 1);
for (int i = 1; i <= ans[0]; ++i) printf("%d ", ans[i]); fclose(stdin);
fclose(stdout);
return 0;
}

Solution 2

然后还跟 \(Asia\) 学了一个除排序以外 \(O(n)\) 的做法…… Orz

大体思路是:

先从小到大排序,然后一个一个作为 \(a_k\) 向左右两边拓展

拓展到的点打上标记,表示不会从这个点开始拓展

这样的话每个点最多只会被它左边或者右边的一个点拓展到,所以复杂度是 \(O(n)\) 的

为什么是 \(O(n)\) 的呢,考虑三个位置 \(x,y,z\),\(y \leq x, z = x * y\),现用 \(x,y\) 去拓展 \(z\),如果 \(x\) 是 \(y\) 的倍数,那么 \(x\) 会被 \(y\) 拓展到,也就不能再去拓展 \(z\) 了;如果 \(x\) 不是 \(y\) 的倍数,\(x\) 拓展到 \(y\) 就会停止,也不会去拓展 \(z\)。

由此可知,每个元素只会被它左边的一个点和它右边的一个点拓展到,所以除排序外的复杂度为 \(O(n)\)。

C. 交换

Description

给定一个 \(\{0, 1, 2, 3, … , n - 1\}\) 的排列 \(p\)。一个 \(\{0, 1, 2 , … , n - 2\}\) 的排列 \(q\) 被认为是优美的排列,当且仅当 \(q\) 满足下列条件:

对排列 \(s = \{0, 1, 2, 3, ..., n - 1\}\) 进行 \(n – 1\) 次交换。

  1. 交换 \(s[q_0],s[q_0 + 1]\)
  2. 交换 \(s[q_1],s[q_1 + 1]\)

最后能使得排列 \(s = p\)。

问有多少个优美的排列,答案对 \(10^9+7\) 取模。\(n \leq 50\)

Solution

一个很厉害的计数\(DP\)。

考虑倒着处理, 比如交换 \((i, i + 1)\), 那么前面的所有数不管怎么交换都无法到后面去,后面的数也是一样到不了前面。说明这最后一次交换前,就要求对于所有的 \(x <= i, y > i\),\(p_x<p_y\)。所以交换前左边的数是连续的,右边也是连续的。由于交换前,前面和后面的数是互相不干涉的,所以就归结成了两个子问题。于是我们可以用记忆化搜索来解决这个问题。

设 \(dp[n][low]\) 代表长度为 \(n\),\(H\) 是 \(\{low, low + 1,…,low + n - 1\}\) 的排列,且 \(H\) 是 \(p\) 的子序列,在 \(H\) 上优美序列的个数。

我们枚举交换哪两个相邻元素 \((k,k+1)\), 然后判断 \(k\) 前面的所有数是否都小于后面的所有数,如果是则进行转移

\[dp[n][low] += dp[k][low] * dp[n – k][low + k ] * C(n – 2, k - 1)
\]

即前面的 \(k\) 个元素与后面的 \(n - k\) 个元素是两个独立的子问题,前面是 \(\{low ... low + k - 1\}\) 的排列,后面是 \(\{low + k ... low + n - 1\}\) 的排列,\(C(n - 2, k - 1)\) 代表的是在交换 \((k, k + 1)\) 前左右两边一共还要进行 \(n - 2\) 次交换,而每次交换左边与交换右边是不同方案,这相当于 \(n - 2\) 个位置选择 \(k - 1\) 个位置填入,故还需要乘上 \(C(n - 2, k - 1)\)。

时间复杂度为 \(O(n^4)\)。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std; typedef long long ll;
const int N = 52, Mod = 1e9 + 7;
int n, p[N], dp[N][N], C[N][N]; int Dfs(int len, int low) {
if (dp[len][low] != -1) return dp[len][low];
if (len == 1) return dp[len][low] = 1;
int &res = dp[len][low]; res = 0;
int t[N], m = 0, j, k;
for (int i = 1; i <= n; ++i)
if (p[i] >= low && p[i] < low + len)
t[++m] = p[i];
for (int i = 1; i < m; ++i) {
swap(t[i], t[i + 1]);
for (j = 1; j <= i; ++j)
if (t[j] >= low + i) break;
for (k = i + 1; k <= m; ++k)
if (t[k] < low + i) break;
if (j > i && k > m) {
ll tmp = (ll)Dfs(i, low) * Dfs(m - i, low + i) % Mod;
tmp = tmp * C[m - 2][i - 1] % Mod;
res = (res + tmp) % Mod;
}
swap(t[i], t[i + 1]);
}
return res;
} int main() {
freopen("swap.in", "r", stdin);
freopen("swap.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &p[i]);
memset(dp, -1, sizeof(dp));
for (int i = 0; i <= n; ++i) {
C[i][0] = 1;
for (int j = 1; j <= i; ++j)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % Mod;
}
Dfs(n, 0);
if (dp[n][0] != -1) cout << dp[n][0] << endl;
else puts("0");
fclose(stdin); fclose(stdout);
return 0;
}

「NOIP模拟赛」Round 3的更多相关文章

  1. 「NOIP模拟赛」Round 2

    Tag 递推,状压DP,最短路 A. 篮球比赛1 题面 \(Milky\ Way\)的代码 #include <cstdio> const int N = 2000, xzy = 1e9 ...

  2. 「NOIP模拟赛」数位和乘积(dp,高精)

    统计方案数,要么组合数,要么递推(dp)了. 这是有模拟赛历史以来爆炸最狠的一次 T1写了正解,也想到开long long,但是开错了地方然后数组开大了结果100->0 T3看错题本来简单模拟又 ...

  3. 「CSP-S模拟赛」2019第四场

    「CSP-S模拟赛」2019第四场 T1 「JOI 2014 Final」JOI 徽章 题目 考场思考(正解) T2 「JOI 2015 Final」分蛋糕 2 题目 考场思考(正解) T3 「CQO ...

  4. 「CSP-S模拟赛」2019第三场

    目录 T1 「POI2007」山峰和山谷 Ridges and Valleys 题目 考场思路(几近正解) 正解 T2 「JOI 2013 Final」 现代豪宅 题目 考场思路(正解) T3 「SC ...

  5. 【模拟】HHHOJ#251. 「NOIP模拟赛 伍」高精度

    积累模拟经验 题目描述 维护一个二进制数,支持如下操作 "+" 该数加 11 "-" 该数减 11 "*" 该数乘 22 "\&q ...

  6. Solution -「牛客 NOIP 模拟赛」打拳

    \(\mathcal{Description}\)   现 \(2^n\) 个人进行淘汰赛,他们的战力为 \(1\sim 2^n\),战力强者能战胜战力弱者,但是战力在集合 \(\{a_m\}\) 里 ...

  7. 「CSP-S模拟赛」2019第二场

    目录 T1 Jam的计数法 题目 考场思路(正解) T2 「TJOI / HEOI2016」排序 题目 考场思路(假正解) 正解 T3 「THUWC 2017」随机二分图 题目 考场思路 正解 这场考 ...

  8. 「CSP-S模拟赛」2019第一场

    目录 T1 小奇取石子 题目 考场思路 正解 T2 「CCO 2017」专业网络 题目 考场思路 题解 T3 「ZJOI2017」线段树 题目 考场思路 正解 这场考试感觉很奇怪. \(T1.T2\) ...

  9. 「2018-11-05模拟赛」T5 传送机 解题报告

    5.传送机(sent.*) 问题描述: 黄黄同学要到清华大学上学去了.黄黄同学很喜欢清华大学的校园,每次去上课时总喜欢把校园里面的每条路都走一遍,当然,黄黄同学想每条路也只走一遍. 我们一般人很可能对 ...

随机推荐

  1. K8S(13)监控实战-部署prometheus

    k8s监控实战-部署prometheus 目录 k8s监控实战-部署prometheus 1 prometheus前言相关 1.1 Prometheus的特点 1.2 基本原理 1.2.1 原理说明 ...

  2. 如何实现一个简易版的 Spring - 如何实现 @Component 注解

    前言 前面两篇文章(如何实现一个简易版的 Spring - 如何实现 Setter 注入.如何实现一个简易版的 Spring - 如何实现 Constructor 注入)介绍的都是基于 XML 配置文 ...

  3. 英语能力考试 All In One

    英语能力考试 All In One 托福,雅思,托业 TOEIC 托业考试 Test of English for International Communication (TOEIC) 国际交流英语 ...

  4. Web 页面生命周期 All In One

    Web 页面生命周期 All In One Web Page LifeCycle All In One refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允 ...

  5. TypeScript Errors All In One

    TypeScript Errors All In One 1. Property 'name' has no initializer and is not definitely assigned in ...

  6. vuex & redux

    vuex & redux https://vuex.vuejs.org/ https://github.com/xgqfrms/VAIO/ https://scrimba.com/playli ...

  7. CSS 阴影效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. Dart: puppeteer库

    和node的差不多,只有写API不一样 puppeteer 地址 安装依赖 dependencies: puppeteer: ^1.7.1 下载 chrome-win 到 <project_ro ...

  9. NGK公链:夯实基础设施 实现产业大规模应用

    当前,区块链已经成为全球技术角逐的前沿,大国及科技巨头竞相在该领域布局,引导区块链服务实体经济,激发市场经济活力.据市场相关研究机构预测,2020年,基于区块链的业务将达到1000亿美元. 对于区块链 ...

  10. 一款基于 Web 的通用数据管理工具(转载)

    一款基于 WEB 的通用数据管控工具 - CloudQuery 前言 前段时间,公司因为业务发展,数据量攀升,老板迫切需要一个工具对数据进行精细化管理,一是确实需要精细化管理:二是因为我们公司小,数据 ...