AGC047 简要题解
AGC047 简要题解
A - Integer Product
注意精度问题即可
B - First Second
建颗 trie 树搞一搞即可
C - Product Modulo
考虑用原根解决此问题,乘法变为了加法,因此用桶记录一下 FFT 即可
D - Twin Binary Trees
观察题目性质容易发现树高事 \(\log n\) 的,即使是暴力枚举第一棵树最浅的点复杂度也正确。
因此就暴力枚举,那么一条合法的路径肯定是分别走向两个儿子的,考虑从左儿子走到第二棵树每个节点的贡献,然后再走右儿子找交集即可,注意在更深处有了交集要把父亲的贡献减掉。
E - Product Simulation
好题!用两个简单运算构建起更复杂到操作!
首先你只能有 加 和 比较 两种运算,实现乘法运算。
首先造出 1,\(1 = 0 < (A+B)\) 如果 \(A = B = 0\) 显然数组所有的数都是 0,没有影响的。
计算机是怎样的系统?二进制!所以考虑二进制下的一些运算,有些函数可能与本题无关
And 运算
A & B = 1 < (A + B)
Or 运算
A | B = 0 < (A + B)
Xor 运算
(A < B) + (B < A)
二的整次幂
a[0] = 1;
for (int i = 1;i <= k; i++)
add(a[0], a[0], 0)
二的整次幂乘 0/1(知道幂的大小)
\(ans = (x>0)<<k\)
二进制拆分
倒着枚举最高位,如果当前数 \(x\) 比 \(2^i\) 大,那么让 \(x -= 2^i\)。
减法显然可以移过去变成加法,然后就是一个整次幂乘 0/1 的问题。
乘法操作
先将 A,B 二进制拆分,然后直接乘就行了。
for(int k = 58; k >= 0; k--) {
answer *= 2;
for(int i = 0; i <= k; i++){
int j = k - i;
if(i < 30 && j < 30) {
answer += 1<(a_bits[i]+b_bits[j]);
}
}
}
这样这道题就已经做完了,我们想想其他的操作。
减法操作
显然我们不可能得出负数,所以实际得出的是 \(\max(0,x-y)\)
将 \(x-y\) 二进制拆分式的枚举,然后就是二的整次幂乘 0/1
除法操作
还是二进制拆分式的枚举即可
F - Rooks
也是 nb 题
按照题解的说法,我们分三步走
第一步:考虑 \(\Theta(n^2)\) 算法
先按 x 排序,对于一个点来说,显然能走到的区域是一个区间 \([L,R]\),考虑区间 dp,首先枚举位置 t,\(dp[l][r][0/1]\) 表示从位置 t 开始,已经走过区间 \([l,r]\),目前在左右端点的最小代价是多少,转移显然。
容易发现从一个位置有大量的走不到区间,且两个位置有大量的公共区间。考虑倒着 dp,设 \(dp[l][r][0/1]\) 表示从 \([l, r]\) 开始,已经解决了 \([1,l)\cup(r,n]\) 这部分能到的地方的最小代价。这样就是 \(\Theta(n^2)\) 的了。
第二步:考虑横坐标单调且纵坐标单调
从任意一点出发,显然只有两种情况,走到最右边再回来和走到最左边再回来。
第三步:正解
从中间那个举行出发,只有这两种情况是合法的,手玩一下可以发现其他的情况都会被挡住,这样就转化为了内部是 \(subtask2\),整体做 \(subtask1\) 了
看代码会清楚些
const int N = 200500, M = 1000050;
int t[M], x[N], y[N], tx[N], ty[N];
int lt[N], rt[N], n;
void rankit(int *x) {
memset(t, 0, sizeof(t));
for (int i = 1;i <= n; i++) t[x[i]]++;
for (int i = 1;i <= 1000000; i++) t[i] += t[i-1];
for (int i = 1;i <= n; i++) x[i] = t[x[i]];
}
struct node {
int x, y, id;
bool operator < (const node &i) const {
return x < i.x;
}
}p[N];
#include <map>
map<pair<int, int> , ll> f[2];
inline int abs(int x) { return x > 0 ? x : -x; }
inline int dis(int a, int b) {
int dx = abs(x[p[a].id] - x[p[b].id]);
int dy = abs(y[p[a].id] - y[p[b].id]);
return dx + dy;
}
const ll inf = 1e18;
ll ans[N];
ll DP(int U, int D, int l, int r, int k) {
if (f[k].count(MP(l, r))) return f[k][MP(l, r)];
int L = lt[l-1], R = rt[r+1]; ll ans = inf;
if (l > 1 && (p[l-1].y == D - 1 || p[l-1].y == U + 1)) {
ll t = dis(L, k == 0 ? l : r);
Mn(ans, DP(max(p[L].y, U), min(p[L].y, D), L, r, 0) + t);
}
if (r < n && (p[r+1].y == D - 1 || p[r+1].y == U + 1)) {
ll t = dis(R, k == 0 ? l : r);
Mn(ans, DP(max(p[R].y, U), min(p[R].y, D), l, R, 1) + t);
}
return f[k][MP(l, r)] = (ans == inf ? l - r : ans);
}
int main() {
read(n);
for (int i = 1;i <= n; i++) read(x[i]), read(y[i]), tx[i] = x[i], ty[i] = y[i];
rankit(tx), rankit(ty);
for (int i = 1;i <= n; i++) p[tx[i]] = (node) {tx[i], ty[i], i};
sort(p + 1, p + n + 1);
lt[1] = 1, rt[n] = n;
for (int i = 2;i <= n; i++)
lt[i] = abs(p[i].y - p[i-1].y) == 1 ? lt[i-1] : i;
for (int i = n - 1; i; i--)
rt[i] = abs(p[i+1].y - p[i].y) == 1 ? rt[i+1] : i;
for (int i = 1;i <= n; i++)
ans[p[i].id] = DP(p[i].y, p[i].y, i, i, 0);
for (int i = 1;i <= n; i++) write(ans[i]);
return 0;
}