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;
}
posted @ 2021-11-03 20:09  shuitiangong  阅读(619)  评论(2编辑  收藏  举报