Get On Your Way.
木偶戏
你看台上台下 角色跟着反转
大红的衣衫 配上滑稽妆扮
一唱一和 多少人在围观
乌鸦跟着鼓掌 笑风水轮流转
两语三言 拉扯我的五感
衣带要系紧 不得有碍观瞻
木偶提线 怪事又成一桩美谈
A. [CEOI2016] kangaroo
神奇的 DP,从未涉及的领域。
观察题意,一个合法序列中,一个数两边的两个数要么都比它大,要么都比它小。
我们可以发现,合法的跳的顺序一定是这样的:
我们把每一个符合这种形式的片段看作一个块。
对于这样一个块来说,有三种操作:
- 将某个块的元素加一
- 添加一个新的块
- 将两个相邻的块合并
我们记 \(dp_{i,j}\) 表示放了 \(i\) 个元素,形成了 \(j\) 个块的方案数。
那么考虑转移,即上面对于块的三种操作。
1. 将某个块的元素加一
块的数量没有发生变化,放的元素多了 \(1\) 个,对于 \(j\) 个块,可以任选其中一个块放入,有
\[dp_{i,j}=j \times dp_{i-1,j} \]考虑加元素之前的值一定是小于该元素的,以后加入的值一定是大于该元素的。
2. 添加一个新的块
原先有 \(j-1\) 个块,即有 \(j\) 个空,选择任意一个空插入块。
\[dp_{i,j}=j \times dp_{i-1,j-1} \]3. 合并两个相邻的块
与添加新块的方式类似。考虑不可能选择第 \(0\) 个块和第 \(n + 1\) 个块与其他块进行合并,所以我们仍然只有 \(j\) 个空。
\[dp_{i,j}=j\times dp_{i-1,j+1} \]添加的用于合并的块的值一定是大于两边的值的。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2050;
const int Mod = 1e9 + 7;
int n,s,t;
long long dp[N][N];
int main() {
cin >> n >> s >> t;
dp[1][1] = 1;
for(int i = 2;i <= n; i++) {
for(int j = 1;j <= i; j++) {
if(i == s || i == t)
dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] + dp[i - 1][j]) % Mod;
else {
dp[i][j] = (dp[i - 1][j - 1] * (j - (i > s) - (i > t)) + dp[i - 1][j + 1] * j) % Mod;
}
}
}
cout << dp[n][1] << "\n";
return 0;
}
B. [JOI 2023 Final] Advertisement 2
题目大意
在一个数轴上有 \(n\) 个人,第 \(i\) 个人位于坐标 \(X_i\),权值为 \(E_i\)。我们要送给一些人书,当 \(i\) 收到了一本书,那么对于所有 \(j\),满足 \(\left | X_i-X_j \right | \le E_i-E_j\),那么 \(j\) 会去买一本书。问最少送几个人书会使得所有人都有一本书。
思路
我们可以考虑把题目中给出的限制转化一下。
\[\begin{aligned} \left | X_i-X_j \right | \le E_i-E_j & \Longleftrightarrow X_i-X_j\leq E_i - E_j \land X_j - X_i \leq E_i-E_j \\ & \Longleftrightarrow E_j-X_j\leq E_i - X_i \land X_j+E_j\leq X_i+E_i \end{aligned} \]那么我们考虑把每个居民转化成平面直角坐标系中的一个点。
我们用 \(E-X\) 当作横坐标,\(E+X\) 当作纵坐标,显然我们可以发现,从原点到这个居民所在点形成的矩形是该居民影响的范围。
我们还可以发现,从左到右看,选取的点的纵坐标是单调递减的,因为如果某个点右上方还有一个点,我们会直接选择那个点而放弃当前点。
按照纵坐标从上往下扫,如果一个点的横坐标大于目前扫过的最大的横坐标时,会对答案产生贡献。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 705000;
int n;
long long ans = 0;
struct Resident{
long long x,y;
}t[N];
bool cmp(Resident a,Resident b) {
if(a.y == b.y)
return a.x > b.x;
return a.y > b.y;
}
int main() {
cin >> n;
long long X,E;
for(int i = 1;i <= n; i++) {
cin >> X >> E;
t[i].x = E - X;
t[i].y = E + X;
}
sort(t + 1,t + n + 1,cmp);
long long Max = LLONG_MIN >> 4;
for(int i = 1;i <= n; i++) {
if(t[i].x > Max)
ans ++;
Max = max(Max,t[i].x);
}
cout << ans << "\n";
return 0;
}
C. Your
一道合成题。
第一问
对于一个 \(n\) 个顶点的凸多边形,它的任何三条对角线都不会交于一点,它的对角线交点的个数是 \(\dfrac{n(n-1)(n-2)(n-3)}{24}\)。
考虑以下几点:
- 每两个顶点连一条对角线;
- 每两条对角线有一个顶点;
- 所以每一个交点对应 \(4\) 个顶点。
那么我们就可以把问题转化为:
从 \(n\) 个顶点中选出 \(4\) 个的方案数。
所以答案为 \({n \choose 4}\),可以转化为 \(\dfrac{n(n-1)(n-2)(n-3)}{24}\)。
第二问
欧拉公式:令 \(G\) 为一个连通的平面图,其中 \(V\) 是顶点的集合,\(E\) 是边的集合,并令 \(F\) 为面的集合,那么有 \(\left | V \right | - \left | E \right |+\left | F \right |=2\)。这里的 \(\left | F \right |\) 包含所有边之外的无穷平面。
明显这不是一个平面图,我们需要将其转化。
转化成平面图后,考虑第二问让我们求的区域数实际上就是除去无穷平面外的平面数,那么问题就转化为求边数和点数。
我们将相交的线段全部拆开,把交点处转化成新的点。
假如原本一条线上有 \(3\) 个交点,现在就把这 \(3\) 个交点变成 \(3\) 个点,一条线段被分割成了 \(4\) 条短线段。
那么转化后的点数包含圆上的点和圆内的交点,即 \(n+{n \choose 4}\)。
考虑转化后的边数,原来的 \({n \choose 2} + 2{n \choose 4} + n\) 个交点每个交点会让边的个数增加 \(2\)。
最后用欧拉公式计算一下,去掉无穷平面,答案为 \({n \choose 4} + {n \choose 2} + 1\)。
Code
#include <bits/stdc++.h>
using namespace std;
const long long Mod = 998244353;
int n;
long long ans1,ans2;
long long Inv(long long x) {
if(x == 4)
return 748683265;
if(x == 6)
return 166374059;
if(x == 2)
return 499122177;
}
const long long INV = Pow(24,Mod - 2) % Mod;
int main() {
cin >> n;
ans1 = 1ll * n * (n - 1) % Mod * (n - 2) % Mod * (n - 3) % Mod * INV % Mod;
ans2 = 1ll * n * (n - 1) % Mod * (n - 2) % Mod * (n - 3) % Mod * INV % Mod + 1ll * n * (n - 1) / 2 + 1;
ans2 %= Mod;
cout << ans1 << "\n" << ans2;
return 0;
}
D. [ARC141F] Well-defined Abbreviation
AC 自动机,咕咕咕。
\[\text{END} \] 标签:right,21,int,long,dp,交点,CSP,模拟,Mod From: https://www.cnblogs.com/baijian0212/p/csp-21.html