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

这题的想法真的很妙啊。
看到题的第一眼,我先想到树链剖分,并把\(DFS\)序当成一段区间上主席树。但是会发现在询问的时候,可能会非常复杂,因为你需要把路径拆成很多条轻链和重链,它们还不一定连续,很难做(这个做法貌似可以用于子树第\(k\)大问题)。
于是我们换一个思路,让某个点的从它的父亲继承信息,也就是让这个结点对应的主席树维护一条从它到根的链。
那查询该如何是好?假设要查询的结点为\(x,y\),我们利用树上差分的思想让\(x,y,lca(x,y),fa[lca(x,y)]\)对应的四棵主席树一起向下跳就行了。
有连边操作时,就要用到启发式合并了,也就是把小的那棵树合并到大的中,暴力重构一下小树的倍增数组和主席树就行了。
代码:

#include <bits/stdc++.h>

using namespace std;

#define N 100000
#define pb push_back
#define mp make_pair
#define mid ((l+r)>>1)
#define pii pair<int, int>
#define D 17

int n0, n, m, q, T, root[N+5], sz[N+5], d[N+5], me[N+5];
int w0[N+5], w[N+5], as[N+5], f[N+5][20];
int nid, sumv[100*N+5], lson[100*N+5], rson[100*N+5];
int lastans;
vector<int> G[N+5];

void build(int &o, int l, int r)
{
    o = ++nid;
    if(l == r) return ;
    build(lson[o], l, mid), build(rson[o], mid+1, r);
}

int LCA(int x, int y)
{
    if(d[x] < d[y]) swap(x, y);
    for(int i = D; i >= 0; --i)
        if(d[f[x][i]] >= d[y]) x = f[x][i];
    if(x == y) return x;
    for(int i = D; i >= 0 && f[x][0] != f[y][0]; --i)
        if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
    return f[x][0];
}

void insert(int o, int &u, int l, int r, int x, int k)
{
    u = ++nid;
    lson[u] = lson[o], rson[u] = rson[o], sumv[u] = sumv[o]+k;
    if(l == r) return ;
    if(x <= mid) insert(lson[o], lson[u], l, mid, x, k);
    else insert(rson[o], rson[u], mid+1, r, x, k);
}

int query(int x, int y, int k) //x-->y 第k大
{
    int lca = LCA(x, y), fa = f[lca][0], l = 1, r = n0;
    x = root[x], y = root[y], lca = root[lca], fa = root[fa];
    while(l != r)
    {
        int t = sumv[lson[x]]+sumv[lson[y]]-sumv[lson[fa]]-sumv[lson[lca]];
        if(t >= k) r = mid, x = lson[x], y = lson[y], fa = lson[fa], lca = lson[lca];
        else k -= t, l = mid+1, x = rson[x], y = rson[y], fa = rson[fa], lca = rson[lca];
    }
    return as[l];
}

void dfs(int u, int fa, int anc)
{
    d[u] = d[fa]+1, sz[u] = 1, f[u][0] = fa, me[u] = anc;
    for(int i = 1; i <= D; ++i) f[u][i] = f[f[u][i-1]][i-1];
    insert(root[fa], root[u], 1, n0, w[u], 1);
    for(int i = 0, v; i < G[u].size(); ++i)
    {
        v = G[u][i];
        if(v == fa) continue;
        dfs(v, u, anc);
        sz[u] += sz[v];
    }
}

void link(int x, int y)
{
    G[x].pb(y), G[y].pb(x);
    if(sz[me[x]] < sz[me[y]]) dfs(x, y, me[y]);
    else dfs(y, x, me[x]);
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("testdata.in", "r", stdin);
        freopen("testdata.out", "w", stdout);
    #endif
    scanf("%d", &T);
    scanf("%d%d%d", &n, &m, &q);
    for(int i = 1; i <= n; ++i) scanf("%d", &w0[i]), w[i] = w0[i]; //离散化
    sort(w0+1, w0+n+1);
    n0 = unique(w0+1, w0+n+1)-w0-1;
    for(int i = 1, t; i <= n; ++i)
    {
        t = lower_bound(w0+1, w0+n0+1, w[i])-w0;
        as[t] = w[i], w[i] = t;
    }
    for(int i = 1, x, y; i <= m; ++i)
    {
        scanf("%d%d", &x, &y);
        G[x].pb(y), G[y].pb(x);
    }
    build(root[0], 1, n0);
    for(int i = 1; i <= n; ++i)
        if(!d[i]) dfs(i, 0, i);
    char c;
    for(int i = 1, x, y, z; i <= q; ++i)
    {
        cin >> c;
        scanf("%d%d", &x, &y);
        x ^= lastans, y ^= lastans;
        if(c == 'Q') scanf("%d", &z), z ^= lastans, printf("%d\n", lastans = query(x, y, z));
        else link(x, y);
    }
    return 0;
}

转载于:https://www.cnblogs.com/dummyummy/p/10106401.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值