首页 > 其他分享 >[Ynoi2004] rpmtdq 题解

[Ynoi2004] rpmtdq 题解

时间:2023-12-17 14:55:35浏览次数:40  
标签:rt int 题解 rpmtdq top2 Ynoi2004 stk ll dis

人生第一发 \(Ynoi\) 的题, 写一篇题解庆祝一下

传送门

我们可以发现, 对于二元组 \((x, y)\) , 若存在一个 \(dist(i, j) \le dist(x, y), x < i < j < y\) 那么答案肯定不是二元组 \((x, y)\)

我们可以考虑把这些肯定不是的点剔除掉

考虑怎么找, 我们可以先点分治, 求出 每个点到 当前分治中心 \(root\) 的 \(dist(root, v)\) 记为 \(dis(v)\)

对于 \(i\) 与 \(j\) 这一对点对, ( \(i < k < j\) )

有 \(dis[i] + dis[j] < dis[k] + dis[j]\) 即 \(dis[i] < dis[k]\) 时,

并且 \(dis[i] + dis[j] < dis[i] + dis[k]\) 即 \(dis[j] < dis[k]\) 时,

它才有可能成为答案, 其余情况都不能成为答案

简单来说就是 两边的都小于中间, 我们可以规定 \(dis[j] > dis[i]\) 对于 \(j\) 来说, 我们要找的就是第一个 小于等于 \(dis[j]\) 的 \(dis[i]\) , 我们可以用单调栈实现, 对于 \(dis[i] > dis[j]\) 的情况, 可以反着再跑一边单调栈

复杂度: \(O(nlog^2n)\)

点分治一共 \(logn\) 层, 每一层每个点会贡献一次, 所以点对数量为 \(nlogn\) 个

点对的真实值可以用 \(lca\) 和差分求一下

最后将询问离线, 做类似于扫描线的二维数点就可以了

可以用树状数组实现

树状数组的实现有一点不一样

修改最小值向下传递, 查询最小值向上传递, 因为我要查询 \(x - r\) , 所以反了过来

这道题最关键的一点是 要发现, 其中有一些点是肯定不能成为答案的, 这样可以大大缩减点对的数量


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair < ll, ll > pai;
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int INF = 1e9;
const ll INF_ = 1e18;

int n, q, head[N], egtot, siz[N], top;
bool del[N];
ll ans[M];
pai stk[N], stk2[N];
int top2;
vector < int > use[N];

struct Edge {
    int to, nxt, w;
} a[N << 1];
struct Query {
    int l, id;
};

vector<Query>qr[N];

inline ll read() {
	ll x = 0;
	char ch = getchar();
	while (!isdigit(ch)) ch = getchar();
	while (isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return x;
}

void add(int u, int v, int w) {

    a[++egtot].to = v;
    a[egtot].nxt = head[u];
    a[egtot].w = w;
    head[u] = egtot;
}

void push(int u, int v) {
    if (u > v)
        swap(u, v);
    use[v].push_back(u);
}

pai findroot(int rt, int f, int pre) {
    siz[rt] = 1;
    int mx = 0;
    pai ans = make_pair(INF, 0);
    for (int i = head[rt]; i; i = a[i].nxt) {
        int t = a[i].to;
        if (del[t] || t == f)
            continue;
        ans = min(ans, findroot(t, rt, pre));
        siz[rt] += siz[t];
        mx = max(mx, siz[t]);
    }
    ans = min(ans, make_pair(max(ll(mx), ll(pre - siz[rt])), ll(rt)));
    return ans;
}

void dfs(int rt, int f, ll s) {
    stk[++top] = make_pair(rt, s);
    for (int i = head[rt]; i; i = a[i].nxt) {
        int t = a[i].to;
        if (t == f || del[t])
            continue;
        dfs(t, rt, s + a[i].w);
    }
}

void solve(int rt, int pre) {
    int root = findroot(rt, 0, pre).second;
    int res = siz[rt];
    del[root] = 1;
    top = 0;
    for (int i = head[root]; i; i = a[i].nxt) {
        int t = a[i].to;
        if (del[t])
            continue;
        dfs(t, root, a[i].w);
    }
    stk[++top] = make_pair(root, 0);
    sort(stk + 1, stk + top + 1);
    top2 = 0;
    for (int i = 1; i <= top; i++) {
        int id = stk[i].first;
        ll d = stk[i].second;
        while (top2 && stk2[top2].second > d)
            top2--;
        push(id, stk2[top2].first);
        stk2[++top2] = stk[i];
    }
    top2 = 0;
    for (int i = top; i >= 1; i--) {
        int id = stk[i].first;
        ll d = stk[i].second;
        while (top2 && stk2[top2].second > d)
            top2--;
        push(id, stk2[top2].first);
        stk2[++top2] = stk[i];
    }
    for (int i = head[root]; i; i = a[i].nxt) {
        int t = a[i].to;
        if (del[t])
            continue;
        solve(t, res);
    }
}

int dep[N], ind[N], lg[N << 1], cnt, st[20][N << 1];
ll de[N];
void dfs(int rt, int f) {
    dep[rt] = dep[f] + 1;
    st[0][++cnt] = rt, ind[rt] = cnt;

    for (int i = head[rt]; i; i = a[i].nxt) {
        int t = a[i].to;
        if (t == f)
        	continue;
        de[t] = de[rt] + a[i].w;
        dfs(t, rt);
        st[0][++cnt] = rt;
    }
}

int cmp(int x, int y) {
    return dep[x] < dep[y] ? x : y;
}

void init() {
    dfs(1, 0);
    for (int i = 1; (1 << i) <= cnt; i++)
        for (int j = 1; j + (1 << i) - 1 <= cnt; j++)
            st[i][j] = cmp(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);
    for (int i = 2; i <= cnt; i++)
        lg[i] = lg[i >> 1] + 1;
}

int LCA (int x, int y) {
    x = ind[x], y = ind[y];
    if (x > y)
        swap(x, y);
    int i = lg[y - x + 1];
    int p = 1 << i;
    return cmp(st[i][x], st[i][y - p + 1]);
}

ll dis(int x, int y) {
    return de[x] + de[y] - 2 * de[LCA(x, y)];
}

struct Tree {
    ll t[N];
    int lowbit (int x) {
    	return x & (-x);
	}
	
    void init() {
        for (int i = 1; i <= n; i++)
            t[i] = INF_;
    }
    
    void add(int p, ll v) {
        for (int i = p; i; i -= lowbit(i))
            t[i] = min(t[i], v);
    }
    
    ll query(int p) {
        ll ans = INF_;
        for (int i = p; i <= n; i += lowbit(i))
            ans = min(ans, t[i]);
        return ans;
    }
} T;

int main() {
    n = read();

    for (int i = 1, u, v, w; i < n; i++) {
        u = read(); v = read(); w = read();
        add(u, v, w), add(v, u, w);
    }

    solve(1, n);
    init();
    T.init();
    q = read();

    for (int i = 1, l, r; i <= q; i++) {
        l = read(); r = read();
        if (l >= r) ans[i] = -1;
        else qr[r].push_back((Query) {l, i});
    }

    for (int i = 1; i <= n; i++) {
        sort(use[i].begin(), use[i].end());
        use[i].resize(unique(use[i].begin(), use[i].end()) - use[i].begin());
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < int(use[i].size()); j++)
            T.add(use[i][j], dis(use[i][j], i));
        for (int j = 0; j < int(qr[i].size()); j++)
            ans[qr[i][j].id] = T.query(qr[i][j].l);
    }

    for (int i = 1; i <= q; i++)
        printf("%lld\n", ans[i]);
    return 0;
}


标签:rt,int,题解,rpmtdq,top2,Ynoi2004,stk,ll,dis
From: https://www.cnblogs.com/aqz180321/p/17909067.html

相关文章

  • AT_abc333_e [ABC333E] Takahashi Quest 题解
    AT_abc333_e[ABC333E]TakahashiQuest题解思路解析可以发现一瓶药水无论什么时候拿被使用掉的时间都是不会变的,所以如果我们想让一瓶药水再背包里待得时间尽可能的短就要让它尽可能的被晚拿起来,于是我们就可以想到使用栈存下每一瓶同类的药水分别出现的时间,此时每遇到一只怪......
  • 2022年RHCE认证考题解析最新版—RH294环境【转】
    由于本人10.17已成功考过CSA,经过两周所学的ansible并结合题库整理出来的CE解析版我也是11月月底就要考了,不过这套解析也是可以满足今年的redhat8题库文中可能涉及一些命令的参数解释,如有不懂的伙伴可参考我的笔记Ansibleps:一切模板似的题库考试,都需要经过大脑的理解方可顺利上......
  • VMware workstation中安装的centos虚拟机ip自动获取可以上网,设置静态ip不能上网问题解
    一、需求   linux中我们会设置hosts文件,这会涉及ip和域名的设置,但是如果虚拟机自动获取ip地址的话,这就意味着之前设置的hosts文件需要重新修改,所以我们需要设置虚拟机为静态ip地址。二、故障现象   我linux虚拟机最开始是自动获取的ip地址,用的nat模式,是可以上网的,......
  • 一道很不错的高中数学题的题解解析
    引:上周六上午把一道高中的数学竞赛题(一道8分的填空题,原题如下图所示)当成一道大题(如上)郑重其事地和孩子以互动的方式探讨了这个题的题解分析. 这是一道出得很好的题.其题解所涉及的知识不超出高一目前所学内容,因此高一的学生也是可能做得出来的.但这题是一道很综合的题,涉......
  • 【理论篇】SaTokenException: 非Web上下文无法获取Request问题解决 -理论篇
    在我们使用sa-token安全框架的时候,有时候会提示:SaTokenException:非Web上下文无法获取Request错误截图:在官方网站中,查看常见问题排查:错误追踪:跟着源码可以看到如下代码:从源码中,我们可以看到,由于非Web上下文中无法直接获取HttpServletRequest对象,因此无法直接在子线程中使用SA-Token......
  • ABC311G One More Grid Task 题解
    给出\(n\timesm\)的矩阵\(a\)。求权值最大子矩形的权值。一个矩形的权值定义为它里面全部数的和乘上最小值。\(n,m\leq300,0\leqa_{i,j}\leq300\)。枚举最小的数\(a_{i,j}\)。则在满足\(a_{i,j}\)是最小值时,包含\((i,j)\)的矩形一定是极大的。这些矩形不好枚举,......
  • SP21690 POWERUP - Power the Power Up 题解
    题目传送门前置知识扩展欧拉定理解法直接对\(a\)和\(b^c\)分讨,跑一遍扩展欧拉定理就行了。另外由于本题的特殊规定\(0^0=1\),故需要在当\(a=0\)时,对\(b^c\)进行判断。手模几组样例,发现结论挺显然的。代码#include<bits/stdc++.h>usingnamespacestd;#definell......
  • SP10050 POWTOW - Power Tower City 题解
    题目传送门前置知识扩展欧拉定理解法本题幂塔是有限层的,这里与luoguP4139上帝与集合的正确用法中的无限层幂塔不同,故需要在到达递归边界\(n+1\)时进行特殊处理,对于处理\(\varphi(p)\)在递归过程中等于\(1\)的情况两题基本一致。回忆扩展欧拉定理中的\(b\)和\(\v......
  • P1405 苦恼的小明 题解
    题目传送门前置知识扩展欧拉定理解法本题幂塔是有限层的,这里与luoguP4139上帝与集合的正确用法中的无限层幂塔不同,故需要在到达递归边界时进行特殊处理,对于处理\(varphi(p)\)在递归过程中等于\(1\)的情况两题基本一致。回忆扩展欧拉定理中的\(b\)和\(\varphi(p)\)......
  • CF1804F Approximate Diameter 题解
    题目链接点击打开链接题目解法很有意思的题,但不难首先一个显然的结论是:算着边的加入,直径长度递减第一眼看到误差范围是2倍,可以想到二分可以观察到如果取答案为\(\frac{n}{2}\)可以覆盖到\(\frac{n}{4}\)(上下取整不重要),那这样每次可以把值域范围缩小4倍,然后只要二分直......