HBCPC2021-Contest 部分题解
C Cover Master
题目描述
解题思路
我们把图中红色的区间称为关键点。可以发现树上每个关键点之间是没有祖先关系的,当两个关键点向上走到它们的lca的时候,会合并成一个点,并且向上合并的过程中,最左边的关键点和最右边的关键点一定会随之往上升。可以先用dfs把询问区间涉及的关键的点及其lca建成一棵树(大概可以称之为虚树?),然后枚举树上最左边和最右边的两个分支上的点,记录一下这些点包含多少关键点,以及他们包含的区间大小,对符合条件的结果取min即可。
代码
const int maxn = 2e6+10;
const int maxm = 2e5+10;
int idx, sz[maxn];
ll n, L, R, k, lv[maxn], rv[maxn];
vector<int> e[maxn], ln, rn;
int dfs(ll l, ll r) {
if (r<L || l>R) return 0;
if (l>=L && r<=R) {
++idx;
lv[idx] = l, rv[idx] = r, sz[idx] = 1;
return idx;
}
ll mid = (l+r)>>1;
int x = dfs(l, mid);
int y = dfs(mid+1, r);
if (!x || !y) return x|y;
else {
++idx;
lv[idx] = l, rv[idx] = r, sz[idx] = sz[x]+sz[y];
e[idx].push_back(x);
e[idx].push_back(y);
return idx;
}
}
void dfsl(int u) {
ln.push_back(u);
if (e[u].empty()) return;
dfsl(e[u][0]);
}
void dfsr(int u) {
rn.push_back(u);
if (e[u].empty()) return;
if (e[u].size()==1) dfsr(e[u][0]);
else dfsr(e[u][1]);
}
void init() {
ln.clear(), rn.clear();
for (int i = 0; i<=idx; ++i) {
e[i].clear();
lv[i] = rv[i] = sz[i] = 0;
}
idx = 0;
}
int main() {
IOS;
int __; cin >> __;
while(__--) {
cin >> n >> L >> R >> k;
init();
int rt = dfs(1LL, n);
dfsl(rt); dfsr(rt);
if (k==1) cout << rv[rt]-lv[rt] << endl;
else {
ll ans = 2e18;
for (auto u : ln)
for (auto v : rn)
if (u!=rt && v!=rt && sz[rt]-sz[u]-sz[v]+2<=k)
ans = min(ans, rv[v]-lv[u]);
cout << ans << endl;
}
}
return 0;
}
F 蒸汽朋克
题目描述
解题思路
这个题题解已经说的很清楚了,就看细节处理的怎么样了,如果用double的话可能有精度问题,按照nb学弟的说法直接存储\(r \times w\)判断的时候就不用卡精度了。
代码
const int maxn = 1e5+10;
const int maxm = 1e5+10;
int n, m, k, vis[maxn], f[maxn];
ll r[maxn], w[maxn], rw[maxn];
double w2[maxn];
vector<int> e[maxn], tmp;
bool wok, isbg;
void dfs(int u) {
tmp.push_back(u);
for (auto v : e[u]) {
if (vis[v]) {
if (vis[v]==vis[u]) isbg = 0;
if (rw[u]+rw[v]!=0) wok = 0;
}
else {
vis[v] = -vis[u];
if (f[v]) {
if (rw[u]+rw[v]!=0) wok = 0;
}
else {
f[v] = 1;
w2[v] = -r[u]*w2[u]/r[v];
rw[v] = -rw[u];
}
dfs(v);
}
}
}
int main() {
IOS;
cin >> n >> m >> k;
for (int i = 1; i<=n; ++i) cin >> r[i];
for (int i = 1, a, b; i<=m; ++i) {
cin >> a >> b;
e[a].push_back(b);
e[b].push_back(a);
}
for (int i = 1; i<=k; ++i) {
int num; cin >> num;
cin >> w[num]; f[num] = 1;
rw[num] = r[num]*w[num];
w2[num] = w[num];
}
bool ok = 1;
for (int i = 1; i<=n; ++i)
if (f[i] && !vis[i]) {
vis[i] = 1;
tmp.clear();
isbg = wok = 1;
dfs(i);
if (!wok) ok = 0;
else if (!isbg) {
for (auto v : tmp)
if (rw[v]) ok = 0;
}
}
bool ok2 = 0;
for (int i = 1; i<=n; ++i)
if (!vis[i]) {
vis[i] = 1;
tmp.clear();
isbg = 1;
dfs(i);
if (isbg) ok2 = 1;
else if (!isbg) {
for (auto v : tmp)
if (rw[v]) ok = 0;
}
}
if (!ok) {
cout << "It is not steampunk!";
return 0;
}
if (ok2) {
cout << "oo";
return 0;
}
cout << "Steampunk!" << endl;
for (int i = 1; i<=n; ++i) cout << fixed << setprecision(4) << w2[i] << (i==n ? "":" ");
return 0;
}
L 变进制四舍五入
题目描述
解题思路
这个题做的时候看样例猜到最后一步肯定是x在y进制下就1位,并且可以进到第2位,这样就变成y了。而x小于y的时候可以把x变成2x-1,但是一直没想出来在x大于y的时候怎么尽可能的把x变小,唉,还是太菜了。
代码
const int maxn = 2e3+10;
const int maxm = 1e5+10;
vector<P> ans;
int main() {
IOS;
ll x, y; cin >> x >> y;
while(x>y) {
ans.push_back({(x*2+2)/3, 2});
x = (x*2+2)/3;
}
while(x<=y/2) {
ans.push_back({x*2-1, 2});
x = x*2-1;
}
if (x<y) ans.push_back({y, 2});
cout << ans.size() << endl;
for (auto v : ans) cout << v.x << ' ' << v.y << endl;
return 0;
}