P3302 [SDOI2013]森林 主席树+LCA+启发式合并

原题链接:https://www.luogu.com.cn/problem/P3302

题意

一个森林由n个节点m条边组成,满足下列两种操作

  1. Q x y k 查询x到y路径上权值第k小的点
  2. L x y 将x和y之间连一条边

注意本题强制在线

分析

看到动态建边操作,很多人会想到LCT,这题应该是可以写的,但我不会

其实这题就是P2633 Count on a tree的加强版,多了一个动态建边的过程。在那道题中,求树上路径的第k小点时,左子树的大小可以转化为
s i z [ u ] + s i z [ v ] − s i z [ l c a ] − s i z [ f a l c a ] siz[u]+siz[v]-siz[lca]-siz[falca] siz[u]+siz[v]siz[lca]siz[falca]
其中falca是lca的父节点,如果lca没有父节点,应该初始化为0。

然后就是加边操作,因为题目已经保证这是一个森林,因此不会成环,我们可以放心用并查集找到根节点,但不能路径压缩,因为我们需要知道整棵树的结构,这时候就可以用到启发式合并了。我们将siz较小的树并到siz较大的树上可以保证时间复杂度在NlogN。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
const int M = 1e5 + 10;
const int MOD = 998244353;
int a[N], b[N], n, m, k, tot, cnt;
int dep[N], f[N][20], vis[N], siz[N], fa[N];
int rt[N];
vector<int> g[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
struct node {
    int ls, rs;
    int sum;
}hjt[N * 50];
int Hash(int x) {
    return lower_bound(b + 1, b + cnt + 1, x) - b;
}
void modify(int &now, int pre, int l, int r, int pos) {
    now = ++tot;
    hjt[now] = hjt[pre];
    hjt[now].sum = hjt[pre].sum + 1;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) modify(hjt[now].ls, hjt[pre].ls, l, mid, pos);
    else modify(hjt[now].rs, hjt[pre].rs, mid + 1, r, pos);
}
int query(int u, int v, int lca, int falca, int l, int r, int K) {
    if (l == r) return b[l];
    int num = hjt[hjt[u].ls].sum + hjt[hjt[v].ls].sum - hjt[hjt[lca].ls].sum - hjt[hjt[falca].ls].sum;
    int mid = (l + r) >> 1;
    if (K <= num) return query(hjt[u].ls, hjt[v].ls, hjt[lca].ls, hjt[falca].ls, l, mid, K);
    else return query(hjt[u].rs, hjt[v].rs, hjt[lca].rs, hjt[falca].rs, mid+1, r, K-num);
}
void dfs(int x, int far, int Rt) {
    dep[x] = dep[far] + 1; f[x][0] = far;
    for (int i = 1; i <= 17; i++) f[x][i] = f[f[x][i-1]][i-1];
    siz[Rt]++; fa[x] = far;
    vis[x] = 1;
    modify(rt[x], rt[far], 1, cnt, Hash(a[x]));
//    cout << x << " " << Hash(a[x]) << endl;
    for (auto v : g[x]) {
        if (v == far) continue;
        dfs(v, x, Rt);
    }
}
int lca(int x, int y) {
    if (dep[x] > dep[y]) swap(x, y);
    for (int i = 17; i >= 0; i--) if (dep[y] - dep[x] >= (1 << i)) y = f[y][i];

    if (x == y) return x;
    for (int i = 17; i >= 0; i--) {
        if (f[y][i] != f[x][i])
            y = f[y][i], x = f[x][i];
    }
    return f[x][0];
}
void solve () {
    int T; cin >> T;
    T = 1; while (T--) {
        cin >> n >> m >> k;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            b[i] = a[i];
            fa[i] = i;
        }
        for (int i = 1; i <= m; i++) {
            int u, v; cin >> u >> v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        sort(b + 1, b + n + 1);
        cnt = unique(b + 1, b + n + 1) - b - 1;
        for (int i = 1; i <= n; i++) {
            if (!vis[i]) {
                dfs(i, 0, i);
                fa[i] = i;
            }
        }
        int lastans = 0;
        for (int i = 1; i <= k; i++) {
            char op;
            int x, y, rk;
            cin >> op;
            if (op == 'Q') {
                cin >> x >> y >> rk;
                x ^= lastans, y ^= lastans, rk ^= lastans;
                int _lca = lca(x, y);
                int fa_lca = f[_lca][0];
                lastans = query(rt[x], rt[y], rt[_lca], rt[fa_lca], 1, cnt, rk);
                cout << lastans << endl;
            } else {
                cin >> x >> y;
                x ^= lastans, y ^= lastans;
                g[x].push_back(y);
                g[y].push_back(x);
                int fx = find(x);
                int fy = find(y);
                if (siz[fx] > siz[fy]) {
                    swap(fx, fy);
                    swap(x, y);
                }
                dfs(x, y, fy);

            }
        }
    }
}
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于微信小程序的家政服务预约系统采用PHP语言和微信小程序技术,数据库采用Mysql,运行软件为微信开发者工具。本系统实现了管理员和客户、员工三个角色的功能。管理员的功能为客户管理、员工管理、家政服务管理、服务预约管理、员工风采管理、客户需求管理、接单管理等。客户的功能为查看家政服务进行预约和发布自己的需求以及管理预约信息和接单信息等。员工可以查看预约信息和进行接单。本系统实现了网上预约家政服务的流程化管理,可以帮助工作人员的管理工作和帮助客户查询家政服务的相关信息,改变了客户找家政服务的方,提高了预约家政服务的效率。 本系统是针对网上预约家政服务开发的工作管理系统,包括到所有的工作内容。可以使网上预约家政服务的工作合理化和流程化。本系统包括手机端设计和电脑端设计,有界面和数据库。本系统的使用角色分为管理员和客户、员工三个身份。管理员可以管理系统里的所有信息。员工可以发布服务信息和查询客户的需求进行接单。客户可以发布需求和预约家政服务以及管理预约信息、接单信息。 本功能可以实现家政服务信息的查询和删除,管理员添加家政服务信息功能填写正确的信息就可以实现家政服务信息的添加,点击家政服务信息管理功能可以看到基于微信小程序的家政服务预约系统里所有家政服务的信息,在添加家政服务信息的界面里需要填写标题信息,当信息填写不正确就会造成家政服务信息添加失败。员工风采信息可以使客户更好的了解员工。员工风采信息管理的流程为,管理员点击员工风采信息管理功能,查看员工风采信息,点击员工风采信息添加功能,输入员工风采信息然后点击提交按钮就可以完成员工风采信息的添加。客户需求信息关系着客户的家政服务预约,管理员可以查询和修改客户需求信息,还可以查看客户需求的添加时间。接单信息属于本系统里的核心数据,管理员可以对接单的信息进行查询。本功能设计的目的可以使家政服务进行及时的安排。管理员可以查询员工信息,可以进行修改删除。 客户可以查看自己的预约和修改自己的资料并发布需求以及管理接单信息等。 在首页里可以看到管理员添加和管理的信息,客户可以在首页里进行家政服务的预约和公司介绍信息的了解。 员工可以查询客户需求进行接单以及管理家政服务信息和留言信息、收藏信息等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值