Chinese Round. 是说时间怎么这么友善...... 不是很难. 比赛的时候完成了前4道. 离比赛结束还有40s的时候交了一发D, 然而...... CE了...... 一开始以为是C++11的锅, 结果发现M_PI
这个常量不是标准库里的. GG...... 比赛结束后又交了一发, A掉了......TAT 本场比赛很多题目都是多解的 (我做出的也不是最优解法 TAT), 兹磁一个......D的贪心很妙. E是个不错的DP题. 题解: 5/5 # A. An abandoned sentiment from past 两个长度分别为n,k的序列a,b, 元素两两不同. a中有k个空位. 问是否能以某种顺序把b插入a, 使得a不是单调递增序列. (2≤n≤100, 1≤k≤n)
元素两两不同告诉我们, 当k>1时直接回答 Yes (只需把b倒序插入). 当k=1时, 验证一下即可.
B. An express train to reveries
p是1到n的一个排列. 长度为n的序列a,b各恰有一个元素在对应位置与p不同. 保证a,b至少有一位不同. 求一个可能的p, 保证有解. (2≤n≤1000, 1≤ai,bi≤n)
设ai≠pi, bj≠pj, 那么除了i,j, 其余位置a,b对应元素相等, 且等于p的对应元素. 据此可以得出一或两个空位, 及备选元素. 枚举并验证.
为了得到i,j, 还可以这样判断: a,b中出现了两次的元素和一次都没出现的元素.
C. An impassioned circulation of affection
一个长度为n, 由小写字母组成的字符串, q个询问: 至多改变m个字符, 连续一串c的最大长度是多少? (1≤n≤1500, 1≤q≤2*10^5)
n的范围较小, 提示我们可以大力预处理一波.
先考虑一下怎么验证一个答案. 可以得到长度为k的字符串, 当且仅当存在区间[i,i+k), 其中不是c的字符个数≤m.
长度为k的子串中, 非c的最小个数cnt(c,k), 这个量是关于k非严格单调递增的. 每个长度为k的区间, 向右扩展一位, 非c的个数要么不变, 要么+1. 如果一个区间没有取到cnt(c,k)的最小值, 那么它扩展出来不会优于取到最小值的任一区间[i,i+k). 因此, cnt(c,k+1)=cnt(c,k)或cnt(c,k)+1.
预处理出来, 就可以二分了. 时间复杂度
事实上, 可以直接预处理答案: 非c的个数对应的最大长度k, 取前缀max, 得到非c的个数不超过x, 对应的最大长度.
two-pointer也是可以的.
D. An overnight dance in discotheque
平面上有n个两两不在多于一点相交的圆(xi,yi,ri). 把它们分成两个集合A,B, 一个集合的价值等于平面上被圆面覆盖奇数次的面积之和, 求两集合价值之和的最大值. (1≤n≤1000, -106≤xi,yi≤106, 1≤ri≤10^6)
看到几何题有点虚......然后发现和几何算法关系不大. 可以抽象出一个有根树森林的模型. 令每个点的权值为圆的面积. 则一棵树的价值=奇数层点权和-偶数层点权和 (0-indexed). 设
一个圆可能被很多圆所包含, 哪一个才是它在树上的父亲呢? 发现只用比较面积. 面积最小的一个就是了.
另外有一个贪心算法: 对于每一棵有根树, 将它的根划分到集合B, 其余划分到集合A.
让我们来证明其正确性 (感谢仓鼠大爷的帮助 QwQ 简单起见采用了下面这个数学归纳法版本). 记一个节点集合
首先, 对于
证毕.
如果用贪心算法, 树不用显式地建出来, 只用数一数每个点有多少个祖先.
建树可以做到 std::set
) + 扫描线. 由于圆只有相离和包含关系, 所以竖直方向上的相对顺序不会变化. 把圆分为上半弧和下半弧两个部分. 插入一个上半弧, 如果上方没有弧, 则它是根; 否则, 最靠近它的是上半弧, 则找到了它的父亲; 否则, 找到了它的兄弟. 怎么在竖直方向上比较两道弧呢? 代值计算......本题有两圆相切的情况, 所以用圆的半径作排序的第二关键字.
E. An unavoidable detour for home
求满足以下条件的
先来挖掘一下性质.
依次考虑每个点向之前的点如何连边, 很容易设计出这样的状态:
这样做是
可不可以直接记录还有多少个有待分配的度数 (插头的总数) 呢? 之所以记录点, 是为了防止重边. 由于每个点最多向倒数第二层连一条边, 所以倒数第二层可以直接记录度数; 最后一层还是记录点数. 这样带来了一个变化: 某点连向倒数第二层某 2-plug 的第1条边和第2条边, 算做两种不同的方案了. 那么, 顺其自然, 令每个点的三条边可区分, 最后除以
这样做是
在Editorial里看到, 有
我们不再一个一个添加点, 而是一层一层考虑. 假设已经完成了
出于某种原因, 把上面这段翻译了一下. |
We can conclude that |
Let's add the vertices from left to right one by one. We call an edge with one endpoint that hasn't been decided a plug. Then, what we need to know is the number of the plugs. That's not enough. To avoid multiple edges, we need to record whether two plugs are from the same vertex. So our state is dp[number of vertices][number of 1-plug vertices in the previous layer][number of 2-plug vertices in the previous layer][number of 1-plug vertices in the current layer][number of 2-plug vertices in the current layer] . Only when number of 1-plug vertices in the previous layer = number of 2-plug vertices int the previous layer = 0 , can we open a new layer. This algorithm is |
Since there are at most 1 edge connected to the previous layer for a vertex, we can simplify the state into dp[number of vertices][number of plugs in the previous layer][number of 1-plug vertices in the current layer][number of 2-plug vertices int the current layer] . Now, the plugs from the same vertex become distinguishable, so we need to do some division to avoid repetition. This algorithm is |
Let's add a layer in one time instead of a vertex. Assume that we've finished dp[number of 1-plug vertices in the first layer][number of 2-plug vertices in the first layer][number of vertices in the second layer] . Then, use array dp to collect our final answer. This algorithm is |
Sorry for poor English... |
#define LLFORMAT "I64"
typedef long long ll;
const int MOD = 1e9 + 7, N = 51;
int d[N];
ll f[N][2*N][N][N];
int main()
{
int n;
scanf("%d", &n);
rep (i, 0, n) scanf("%d", d+i);
if (d[1] == 2)
f[1][d[0]-1][1][0] = d[0] * d[1];
else
f[1][d[0]-1][0][1] = d[0] * d[1];
rep (i, 1, n-1) rep (j, 0, 2*n+1) rep (k, 0, n+1) rep (l, 0, n+1)
{
ll now = f[i][j][k][l];
if (now == 0) continue;
if (j > 0)
{
if (d[i+1] == 2)
{
ll tmp = j*2*now;
(f[i+1][j-1][k+1][l] += tmp) %= MOD;
if (k > 0)
(f[i+1][j-1][k-1][l] += tmp * k) %= MOD;
if (l > 0)
(f[i+1][j-1][k+1][l-1] += tmp * l * 2) %= MOD;
}
else
{
ll tmp = j*3*now;
(f[i+1][j-1][k][l+1] += tmp) %= MOD;
if (k > 0)
{
(f[i+1][j-1][k][l] += 2 * tmp * k) %= MOD;
if (k > 1)
(f[i+1][j-1][k-2][l] += tmp * k * (k-1)) %= MOD;
}
if (l > 0)
{
(f[i+1][j-1][k+2][l-1] += tmp * l * 4) %= MOD;
if (k > 0)
(f[i+1][j-1][k][l-1] += tmp * l * 4 * k) %= MOD;
if (l > 1)
(f[i+1][j-1][k+2][l-2] += tmp * l * (l-1) * 4) %= MOD;
}
}
}
else
{
int p = k + 2*l;
if (d[i+1] == 2)
{
ll tmp = p*2*now;
(f[i+1][p-1][1][0] += tmp) %= MOD;
}
else
{
ll tmp = p*3*now;
(f[i+1][p-1][0][1] += tmp) %= MOD;
}
}
}
ll ans = f[n-1][0][0][0];
rep (i, 0, n)
(ans *= (d[i] == 2 ? 500000004 : 166666668)) %= MOD;
printf("%" LLFORMAT "d\n", ans);
return 0;
}