AGC066 题解
题解:AT_agc066_a [AGC066A] Adjacent Difference
笑点解析:没有必要将总成本最小化。
我们将格子间隔的黑白染色(显然有两种染色方法),对于黑点我们要求它是奇数倍 \(d\),对于白点我们要求它是偶数倍 \(d\),这样一定满足相邻格子相差至少 \(d\)。
因为两种染色方法的代价和为 \(dN^2\),所以两种方法中至少有一种满足代价小于 \(\frac{1}{2}dN^2\),容易实现:
ll n, m, ans = 1e15, Case=1;
ll a[N][N], b[N][N], c[N][N];
void solve() {
input(n, m);
for(ll i = 1; i <= n; i++) {
for(ll j = 1; j <= n; j++) {
input(a[i][j]);
}
}
for(ll x = 0; x <= 1; x ++) {
ll sum = 0;
for(ll i = 1; i <= n; i ++) {
for(ll j = 1; j <= n; j ++) {
ll k = a[i][j] / m;
if((i + j + k + x) % 2 == 0) {
if(a[i][j] < 0) k --;
else k ++;
}
b[i][j] = k * m;
sum += abs(b[i][j] - a[i][j]);
}
}
if(sum < ans) {
for(ll i = 1; i <= n; i ++) {
for(ll j = 1; j <= n; j ++) {
c[i][j] = b[i][j];
}
}
ans = sum;
}
}
for(ll i = 1; i <= n; i ++) {
for(ll j = 1; j <= n; j ++) {
print(c[i][j]);
}
putchar('\n');
}
// print(ans);
}
题解:AT_agc066_b [AGC066B] Decreasing Digit Sums
题目大意:给定 \(n\),求一个 \(x\),使得对于任意的 \(1\le i\le n\),满足 \(d(2^{i-1}x)>d(2^ix)\)。其中 \(d(x)\) 指 \(x\) 的数位和。
我们发现,答案是具有单调性的,也就是对于 \(n=50\) 的答案,同样适用于 \(n<50\) 的情况。
所以我们只用考虑 \(n=50\) 的答案即可。
按照直观的感受,给定任意的 \(x\) 与 \(y\),已知 \(x>y\),那么 \(d(x)\) 大于 \(d(y)\) 的概率会比较大。
但是随着 \(i\) 的增加,\(2^ix\) 也会随之增加,所以我们要考虑减小 \(d(2^ix)\)。
我们发现,如果某一个数位为 \(0\),对于 \(d\) 函数就没有贡献。所以考虑构造一个 \(x\),使得它乘上 \(2^i\) 可以产生若干个 \(0\)。所以设 \(x=5^{50}a\),那么 \(d(2^ix)=d(2^i5^{50}a)=d(10^i5^{50-i}a)=d(5^{50-i}a)\),此时随着 \(i\) 的增加,\(5^{50-i}a\) 也会随之减小,那么 \(d(5^{50-i}a)\) 肯定也会趋于减小。具体:
但是这样仍旧不是单调递减的,但是它是趋于单调递减的,所以我们可以考虑把它们拼接起来,这样平均下来,单调递减的概率就会大大上升。
我们随机得到 \(a_1,a_2,\cdots,a_k\),然后将这 \(k\) 个系数组成的数用 \(0\) 连接起来,这样就可以保证它单调递减的概率比较大:
下面代码中的 bign
是高精度,check
是判断是否合法的函数:
ll n = 50, k = 100;
bign a = 1, ans;
int main() {
for(ll i = 1; i <= 50; i ++) a = a * 5;
while(true) {
ans = 0;
for(ll i = 1; i <= k; i ++) {
ll x = rnd() % 100 + 1;
bign tmp = a * x;
for(ll j = 1; j <= tmp.len + 50; j ++) ans = ans * 10;
ans = ans + tmp;
}
if(check(ans)) {
cout << ans << endl;
break;
}
}
return 0;
}
题解:AT_agc066_c [AGC066C] Delete AAB or BAA
这题评黑我觉得有点过了。其实还是比较好想的。
考虑一个区间怎么才能被消除掉:它满足可以分成若干个满足以下条件的子串:每个子串都满足 \(\tt A\) 的数量是 \(\tt B\) 的两倍,左端点或右端点是 \(\tt B\)。
证明可以让时间倒流,我们在这个字符串内不断添加 \(\tt AAB\) 或 \(\tt BAA\)。
如果一个字符串是空字符串,添加 \(\tt AAB\) 或 \(\tt BAA\) 明显符合。
如果一个字符串符合条件,那么在两端添加 \(\tt AAB\) 或 \(\tt BAA\) 相当于添加了一个新的符合条件的子串,如果在中间添加 \(\tt AAB\) 或 \(\tt BAA\),字符串两端的字符仍旧不变,依旧符合。
所以通过归纳法可以证明。
所以我们设 \(x_i\) 表示 \(1\sim i\) 中 \(\tt B\) 的数量的两倍减去 \(\tt A\) 的数量。若 \(x_l=x_r\),则 \([l+1,r]\) 明显是一个可以被消除掉的字符串。
所以我们容易设 \(f_i\) 表示处理到第 \(i\) 个字符,最少不删掉的字符的数量,明显我们需要最小化它。
那么有转移方程:
f_{i-1}+1 \\
f_j & x_i=x_j,s_{j+1}={\tt B} \\
f_j & x_i=x_j,s_{i}={\tt B}
\end{cases}
\]
对于第二、三种情况,我们可以用两个 map
来储存每种 \(x\) 下的最小 \(f\) 值。
这里不使用 \(f_i\) 表示最多不删除的字符数量的原因是因为不太好打。
void solve() {
map<ll, ll> ha, hb;
scanf("%s", s + 1);
n = strlen(s + 1);
for(ll i = 1; i <= n; i ++) {
a[i] = a[i - 1], b[i] = b[i - 1];
if(s[i] == 'A') {
a[i] ++;
}
else {
b[i] ++;
}
x[i] = a[i] - 2 * b[i];
ha[x[i]] = hb[x[i]] = inf;
}
ha[0] = hb[0] = inf;
for(ll i = 1; i <= n; i ++) {
f[i] = inf;
}
if(s[1] == 'A') {
ha[0] = 0;
} else {
hb[0] = 0;
}
for(ll i = 1; i <= n; i ++) {
f[i] = min(f[i], f[i - 1] + 1);
if(s[i] == 'A') {
f[i] = min(f[i], hb[x[i]]);
} else {
f[i] = min(f[i], ha[x[i]]);
f[i] = min(f[i], hb[x[i]]);
}
if(s[i + 1] == 'A') {
ha[x[i]] = min(ha[x[i]], f[i]);
} else {
hb[x[i]] = min(hb[x[i]], f[i]);
}
}
print((n - f[n]) / 3);
putchar('\n');
}
题解:AT_agc066_e [AGC066E] Sliding Puzzle On Tree
笑点解析:P6277。
发现交换操作有可传递性:如果 \((x,y)\) 两个位置(注意我们这里是位置而不是指石子)可以互相交换,\((y,z)\) 两个位置也可以互相交换,那么 \((x,z)\) 两个位置必定是可以互相交换的。因为可以先交换 \((x,y)\),再交换 \((y,z)\),最后再次交换 \((x,y)\)。
我们发现如果当前如果有 \(k\) 个石子,无论它们在哪些位置,我们总是可以移动它们到树上的 \(1\cdots k\) 号位置。也就是说,如果忽略标号的话,任意两个状态都是直达的。
所以我们固定这 \(k\) 个石子在 \(1\cdots k\) 号位置,计算有多少种有标号的排列状态。
假如 \((x,y)\) 两个石子能够在不影响其它石子的情况下交换,我们就给它们连一条边,发现最后面会形成很多个团(完全图)。根据交换操作的可传递性,那么每一个团的情况肯定是可以互相到达的。
我们设这些团大小为 \(s_i\),那么答案就是 \(\binom{n}{k}\times\prod (s_i!)\)。
考虑怎样的情况下两个石子是可以交换的:
首先,我们可以把石子从位置 \(1\cdots k\) 移出来到任意位置。
然后在不影响其它石子的情况下交换两个石子。
最后,按照相反的顺序移动回位置 \(1\cdots k\)。
此时,两个石子就是可交换的,连一条边。
考虑怎样在不影响其它石子的情况下交换两个石子:
发现当在一条没有石子的链且出现一个分支时,两个石子可以交换:
如上,位置 \(1\) 上的石子和位置 \(6\) 上的石子交换时,可以在这个没有石子且有一个分支 \(4\) 的链 \(2\to5\) 上进行交换。先把 \(1\) 上的石子移动到 \(4\) 上,再把 \(6\) 上的石子移动到 \(1\) 上,最后把移动到 \(4\) 上的石子移动到 \(6\) 上。
总结可以交换的规律:
- 没有石子的链;
- 有一个分支(有一个位置度数大于二)。
我们将其拓展一下:
在一个中间都是二度点,两端都是非二度点的链上,设两端不包括链的树的大小分别为 \(a\)、\(b\),链的长度为 \(c\)(它们都包括两端的点):
如上,\(1\to8\) 就是合法的中间都是二度点,两端都是非二度点的链。其中 \(a=5\)(位置 \(1\),\(2\),\(3\),\(4\),\(5\) 都是左端的树),\(b=5\)(位置 \(8\),\(9\),\(10\),\(11\),\(12\) 都是右端的树),\(c=4\)(位置 \(1\),\(6\),\(7\),\(8\) 都是中间的链)。
我们发现,当 \(k\le(a-1)+(b-1)-1\) 时,两端的树上的每一个位置都可以互相交换,也就是能到达左端的石子和能到达右端的石子一定可以交换,我们对这两个石子连边。
原理:将 \(k\) 个石子任意的移动到两颗树上,留下一个空位置作为交换的分支,然后树上的任意两个石子都可以通过这个分支和没有石子的链进行交换。
总结可以交换的规律:\(k<(a-1)+(b-1)\),也就是 \(k<n-c\)。
发现,若 \((x,y)\) 是满足这个条件的一条链,也就是能到达 \(x\) 的石子能和能到达 \(y\) 的石子交换。以及若 \((y,z)\) 也是满足这个条件的一条链,那么能到达 \(x\) 的石子能肯定也能和能到达 \(z\) 的石子交换。
也就是说,我们链的两端可以互相连接形成很多个连通块
我们可以把每个满足交换条件,也就是 \(k<n-c\) 的链的两端连接起来,因为可以到达这两个位置的石子就可以和能到达另一个位置的石子交换,所以说,我们计算能到达这些点的石子的个数,即可得到 \(s_i\),也就是团的大小。
因为当 \(k\) 从小到大枚举时,\(k<n-c\) 也就是 \(c<n-k\) 的边是越来越少的,不好维护。考虑时光倒流,将 \(k\) 从大到小枚举时,\(c<n-k\) 的边将会越来越多,用并查集即可维护。
回到刚刚那个问题,怎么计算能到达这些点的石子的个数?
正難则反,我们可以计算出无法到达这个连通块的石子的个数,再用石子的总数 \(k\) 减去即可。
如果 \(a_i\to b_i\) 不是一条满足上面交换条件的链,设连通块外的树的大小为 \(a_i\),连通块内树的大小为 \(b_i\),链的长度为 \(c_i\)。
那么我们能够进入到这个联通块的最多只有 \(b_i-1\) 个石子,也就是把连通块内树全部填满,那么还有 \(k-(b_i-1)\) 的石子无法与之交换。
那么总共的可到达的节点数量为:
k-\sum_{a_i\to b_i}(k-(b_i-1)) &= k-\sum_{a_i\to b_i}(k-(n-a_i-(c_i-1))) \\
&= k-\sum_{a_i\to b_i}(k-n+a_i+c_i-1) \\
\end{aligned}
\]
发现 \(a_i+c_i-1\) 为“联通块内子树” 外的点减一(\(c_i\) 有一端在连通块内),所以设 \(p_i\) 为链方向连通块外的点的个数,有:
k-\sum_{a_i\to b_i}(k-(b_i-1)) &= k-\sum_{a_i\to b_i}(k-n+p_i+1) \\
&= k-(\sum_{a_i\to b_i}(k-n+1)+\sum_{a_i\to b_i}p_i)
\end{aligned}
\]
设连通块大小为 \(\text{siz}\),不合法的 \(a_i\to b_i\) 个数为 \(\text{out}\)。我们发现子树外的点数和 \(\sum_{a_i\to b_i}p_i=n-\text{siz}\)。有:
k-\sum_{a_i\to b_i}(k-(b_i-1)) &= k-([\text{out}](k-n+1)+n-[\text{siz}]) \\
&= k-[\text{out}](k-n+1)-n+[\text{siz}] \\
&= ([\text{out}]-1)(n-k-1)+[\text{siz}]-1
\end{aligned}
\]
我们维护一下连通块的 \(\text{out}\) 和 \(\text{siz}\) 即可。注意在合并的时候,双方的 \(\text{out}\) 和 \(\text{siz}\) 都会减小!
因为每一次连通块个数等于不合法链的数量加上一,而不合法链只能存活链长的时间,所以均摊之后是 \(O(n)\) 的。连通块使用 set
维护。所以时间复杂度为 \(O(n\log n)\)。精细实现能够做到更优。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 200010
#define P 998244353
ll t, n;
ll deg[N];
ll head[N], nxt[2 * N], to[2 * N], cnt;
ll fa[N], out[N], siz[N];
ll ans[N], fac[N], ifac[N];
set<ll> s;
struct node {
ll a, b, c;
node(ll a = 0, ll b = 0, ll c = 0):a(a), b(b), c(c) {}
} edge[N];
ll tot;
void addEdge(ll u, ll v) {
cnt ++;
to[cnt] = v;
nxt[cnt] = head[u];
head[u] = cnt;
}
ll find(ll x) {
if(fa[x] == x) {
return x;
}
return fa[x] = find(fa[x]);
}
void merge(ll a, ll b, ll c) {
a = find(a), b = find(b);
fa[a] = b;
siz[b] += siz[a] + c - 2;
out[b] += out[a] - 2;
s.erase(a);
}
void dfs(ll u, ll fa, ll rt, ll c) {
if(deg[u] != 2) {
if(rt) {
edge[++ tot] = node(u, rt, c);
}
c = 1;
rt = u;
}
for(ll i = head[u]; i; i = nxt[i]) {
ll v = to[i];
if(v == fa) continue;
dfs(v, u, rt, c + 1);
}
}
ll qpow(ll x, ll y) {
if(y == 0) return 1;
if(y % 2 == 1) return x * qpow(x, y - 1) % P;
ll tmp = qpow(x, y / 2);
return tmp * tmp % P;
}
int main() {
scanf("%lld", &t);
while(t --) {
scanf("%lld", &n);
for(ll i = 1; i <= n; i ++) {
head[i] = 0, deg[i] = 0, fa[i] = 0, siz[i] = 0, out[i] = 0;
}
cnt = 0, tot = 0;
s.clear();
for(ll k = 1; k < n; k ++) {
ll u, v;
scanf("%lld %lld", &u, &v);
addEdge(u, v);
addEdge(v, u);
deg[u] ++;
deg[v] ++;
}
for(ll i = 1; i <= n; i ++) {
if(deg[i] != 2) {
fa[i] = i;
siz[i] = 1;
out[i] = deg[i];
s.insert(i);
}
}
dfs(*s.begin(), 0, 0, 0);
sort(edge + 1, edge + 1 + tot, [&](const auto &x, const auto &y) {
if(x.c == y.c) {
if(x.a == y.a) return x.b < y.b;
else return x.a < y.a;
}
return x.c < y.c;
});
fac[0] = 1;
for(ll i = 1; i <= n; i ++) {
fac[i] = fac[i - 1] * i % P;
}
ifac[n] = qpow(fac[n], P - 2);
for(ll i = n; i >= 1; i --) {
ifac[i - 1] = ifac[i] * i % P;
}
ans[n] = 1;
ll pos = 1;
for(ll k = n - 1; k >= 1; k --) {
while(pos <= tot && k < n - edge[pos].c) {
merge(edge[pos].a, edge[pos].b, edge[pos].c);
pos ++;
}
ans[k] = fac[n] * ifac[k] % P * ifac[n - k] % P;
for(ll i : s) {
(ans[k] *= fac[(out[i] - 1) * (n - k - 1) + siz[i] - 1]) %= P;
}
}
for(ll i = 1; i <= n; i ++) {
printf("%lld ", ans[i]);
}
printf("\n");
}
}
AGC066 题解的更多相关文章
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- noip2016十连测题解
以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...
- BZOJ-2561-最小生成树 题解(最小割)
2561: 最小生成树(题解) Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1628 Solved: 786 传送门:http://www.lyd ...
- Codeforces Round #353 (Div. 2) ABCDE 题解 python
Problems # Name A Infinite Sequence standard input/output 1 s, 256 MB x3509 B Restoring P ...
- 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解
题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...
- 2016ACM青岛区域赛题解
A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- poj1399 hoj1037 Direct Visibility 题解 (宽搜)
http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...
- 网络流n题 题解
学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...
- CF100965C题解..
求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...
- JSOI2016R3 瞎BB题解
题意请看absi大爷的blog http://absi2011.is-programmer.com/posts/200920.html http://absi2011.is-programmer.co ...
随机推荐
- 【风控算法】二、SQL->Python->PySpark计算KS,AUC及PSI
KS,AUC 和 PSI 是风控算法中最常计算的几个指标,本文记录了多种工具计算这些指标的方法. 生成本文的测试数据: import pandas as pd import numpy as np i ...
- vscode 智能提示 函数提示右侧有所在目录 Show Inline Details
vscode 智能提示 函数提示右侧有所在目录 Show Inline Details 有目录提示的 没有目录提示的 默认是有目录提示,我那个配置单啊~ "editor.suggest.sh ...
- WPF之事件
目录 WPF的树形结构 事件 路由事件 使用WPF内置路由事件 自定义路由事件 ButtonBase类的Click路由事件 创建一个路由事件 RoutedEventArgs的Source与Origin ...
- 08.Android之View事件问题
目录介绍 8.0.0.1 简述Android的事件分发机制?dispatchTouchEvent方法的作用是什么?说下View和ViewGroup分发事件? 8.0.0.2 onInterceptTo ...
- 记录--ECharts — 饼图相关功能点(内环、外环、环形间隔、环形文字、轮播动画)
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 记录一下在公司遇到的一些功能,以及相关实现 以上的内容我花了一周时间去实现的,自己也觉得时间很长,但主要因为很少使用ECharts,导致使 ...
- 记录--elementui源码学习之仿写一个el-button
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 本篇文章记录仿写一个el-button组件细节,从而有助于大家更好理解饿了么ui对应组件具体工作细节.本文是elementui源码学习仿写 ...
- 记一起rust悬垂引用解决
最初要写一段从redis的hash获取json字符串,转化为结构体,代码逻辑如下 use redis::{Client, Commands, Connection, Iter}; use serde: ...
- 立创EDA的使用
立创EDA的使用 1.实验原理 最近在使用立创EDA来做电路作业,这里记录一下立创EDA的基本操作,以后小型的电路设计可以在其主页完成.立创EDA是一个可以线上完成电路设计仿真以及布线的免费设计工具, ...
- KGIS支持的扩展介绍
1.postgis postgis的基本核心功能,仅支持地理图形(矢量要素),在其他Extension前启用. 2.postgis_raster 对栅格数据的支持. 3.postgis_topolog ...
- KingbaseES V8R6 集群运维系列--archive_cleanup_command参数应用
案例说明: 参数archive_cleanup_command可以配置在kingbase.conf文件中,用于备库清理不在需要的归档日志,参数详细说明见下图: https://postgresql ...