首页 > 其他分享 >MEX Tree

MEX Tree

时间:2023-10-13 15:49:12浏览次数:35  
标签:sz return int lca1 Tree else lca MEX

MEX Tree

MEX Tree - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

目录

题目大意

给出一棵 \(n\) 个点的树,点从 \(0\) 到 \(n - 1\) 编号。定义一条路径的权值是路径上所有点编号的 \(mex\) 。对于每个 \(0\le i\le n\) 求出 \(mex\) 为 \(i\) 的路径有几条。注意,这里统计的路径需要包括至少一条边。

一个集合的 \(mex\) 定义为最小的不在集合中的非负整数。

基本思路

观察发现,我们在处理 \(i\) 时,\(0\to i - 1\) 必须在一条路径上,否则后面的答案就都是 \(0\)

\(sz_i\) 表示以 \(i\) 为根的子树大小

\(sp\) 表示非 \(0\) 端点所在对于 \(0\) 的对应子树大小

下面会要用到倍增求 \(lca\)

我们就可以搞一种类似于虚树操作的做法。

这里假设 \(0\) 是根

询问

所以询问可以分成 \(3\) 种情况:

  • \(i\) 在当前的路径中
  • \(i\) 是当前路径端点的子孙
  • \(i\) 不属于上面的情况

然后 \(mex = 0\) 和 \(mex = 1\) 是特殊处理一下

当 \(mex = 0\) 时:

只要不经过 \(0\) 的路径都满足条件

所以答案就是 \(0\) 的所有子树里面任意选两个点的路径

当 \(mex = 1\) 时:

在 \(n\) 个点里面任意选两个点的方案数 \(-\) \(mex = 0\) 的方案数

LL gs (LL x) { return x * (x - 1) / 2; }
void pre_ans () {
    ans[1] = gs (sz[0] - sz[1]);
    int y;
    for (int i = hd[0] ; i ; i = e[i].nt) {
        y = e[i].to;
        ans[0] += gs (sz[y]);
        int lca = Lca (y , 1);
        if (lca == y) ans[1] -= gs (sz[y] - sz[1]);
        else ans[1] -= gs (sz[y]);
    }
}

现在我们把其他询问分成两种情况

两个端点都不为 \(0\),两个端点分别为 \(x , y\)

  • \(i\) 在路径上 $lca(x , i) =i \or lca (y , i) = i $

    答案就是 \(0\)

  • \(i\) 是当前路径端点的子孙 \(lca (x , i) = x \or lca (y , i) = y\)

    答案就是对于端点子树大小 \(-\) \(sz_i\) 再乘上另一端子树大小

  • 否则,答案就是 \(sz_x * sz_y\)

有一个点为 \(0\) 的情况,另一个端点是 \(x\)

  • \(i\) 在当前路径中 \(lca (x , i) = i\)

    答案就是 \(0\)

  • \(i\) 是当前路径端点的子孙

    \(i\) 是不为零的那个端点的子孙\(lca (x , i) = x\),那么答案就是: \((siz[x] - siz[i]) * (siz[0] - sp)\)

    \(i\) 是 \(0\) 的子孙 \(lca (x , i) = 0\) ,那么答案就是: \((siz[0] - sp - siz[i]) * siz[x]\)

  • \(i\) 不属于上面的任意一种情况

    那么 \(i\) 就是当前路径的分叉,那么答案就是: \((siz[0] - sp) * siz[x]\)

LL gt_ans (int x) {
    int lca1 = Lca (x , l) , lca2 = Lca (x , r);
    LL ans1 , ans2;
    if (r) {
        ans1 = sz[l] , ans2 = sz[r];
        if (lca1 == x || lca2 == x) return 0;
        else if (lca1 == l) ans1 -= sz[x];
        else if (lca2 == r) ans2 -= sz[x];
    }
    else {
        ans1 = sz[l] , ans2 = sz[r] - sp;
        if (lca1 == x || lca2 == x) return 0;
        else if (lca1 == l) ans1 -= sz[x];
        else if (lca1 == 0) ans2 -= sz[x];
    }
    return ans1 * ans2;
}

修改

尝试将 \(i\) 加入当前路径中

1、两端都不为 \(0\)

  • \(i\) 已经在路径上了 \(lca (x , i) = i \or lca (y , i) = i\)

    就不用管了

  • \(i\) 是其中一端的子孙 $lca(x , i) = x \or lca (y , i) = y $

    直接把那个端点设为 \(i\) 就好了

  • 如果不属于上面的两种情况

    那么就是分叉,直接下面的答案都为 \(0\) 就好了

2、至少有一端是 \(0\) 的情况

  • 如果两端都是 \(0\)

    直接把一端设为 \(i\)

  • \(i\) 已经在路径上了 \(lca (x , i) = i\)

    不用管了

  • \(lca (x , i) = x\)

    \(x = i\)

  • 不属于上面的情况

    1、分叉 \(lca (x , i)\neq 0\) 插入失败

    2、\(x \neq 0\) 且 \(lca (x , i) = 0\) ,那么 \(y = i\)

bool add (int x) {
    int lca1 = Lca (l , x) , lca2 = Lca (r , x);
    if (r) {
        if (lca1 == l) {
            l = x;
            return 1;
        }
        else if (lca1 == x) return 1;
        if (lca2 == r) {
            r = x;
            return 1;
        }
        else if (lca2 == x) return 1;
    }
    else {
        if (lca1 && (lca1 != l && lca1 != x)) return 0;
        else if (lca1 == l ) {
            l = x;
            return 1;
        }
        else if (lca1 == x) return 1;
        else if (lca1) return 0;
        else {
            r = x;
            return 1;
        }
    }
    return 0;
}

code

#include <bits/stdc++.h>
#define fu(x , y , z) for(int x = y ; x <= z ; x ++) 
#define fd(x , y , z) for(int x = y ; x >= z ; x --)
#define LL long long
using namespace std;
const int N = 6e5 + 5 , inf = 2e5;
int n , sz[N] , hd[N] , cnt , fa[N] , sp , dep[N] , f[N << 1][30] , l , r;
LL tw[30] , ans[N] , lg2[inf + 5];
struct E {
    int to , nt;
} e[N << 1];
void add (int x , int y) { e[++cnt].to = y , e[cnt].nt = hd[x] , hd[x] = cnt; }
int Lca (int x , int y) {
    int flg;
    while (dep[x] != dep[y]) {
        flg = 0;
        if (dep[x] > dep[y]) swap (x , y);
        fd (i , 25 , 0) {
            while (dep[f[y][i]] > dep[x]) {
                y = f[y][i];
                flg = 1;
            }
        }
        if (!flg) break;
    }
    while (dep[x] != dep[y]) {
        if (dep[x] > dep[y]) swap (x , y);
        y = f[y][0];
    }
    while (x != y) {
        flg = 0;
        fd (i , 25 , 0) {
            while (f[x][i] != f[y][i]) {
                x = f[x][i] , y = f[y][i];
                flg = 1;
            }
        }
        if (!flg) break;
    }
    while (x != y)
        x = f[x][0] , y = f[y][0];
    return x;
}
void dfs1 (int x) {
    int y;
    sz[x] = 1;
    for (int i = hd[x] ; i ; i = e[i].nt) {
        y = e[i].to;
        if (fa[x] == y) continue;
        fa[y] = x;
        dep[y] = dep[x] + 1;
        dfs1 (y);
        sz[x] += sz[y];
    }
}
void gt_f () {
    fu (i , 0 , n - 1) 
        fu (j , 1 , 25) 
            f[i][j] = 0;
    dep[0] = 1;
    dfs1 (0);
    fu (i , 0 , n - 1) 
        f[i][0] = fa[i];
    fu (j , 1 , 25) {
        fu (i , 0 , n - 1) {
            f[i][j] = f[f[i][j - 1]][j - 1];
        }
    }
}
LL gs (LL x) { return x * (x - 1) / 2; }
void pre_ans () {
    ans[1] = gs (sz[0] - sz[1]);
    int y;
    for (int i = hd[0] ; i ; i = e[i].nt) {
        y = e[i].to;
        ans[0] += gs (sz[y]);
        int lca = Lca (y , 1);
        if (lca == y) ans[1] -= gs (sz[y] - sz[1]);
        else ans[1] -= gs (sz[y]);
    }
}
bool add (int x) {
    int lca1 = Lca (l , x) , lca2 = Lca (r , x);
    if (r) {
        if (lca1 == l) {
            l = x;
            return 1;
        }
        else if (lca1 == x) return 1;
        if (lca2 == r) {
            r = x;
            return 1;
        }
        else if (lca2 == x) return 1;
    }
    else {
        if (lca1 && (lca1 != l && lca1 != x)) return 0;
        else if (lca1 == l ) {
            l = x;
            return 1;
        }
        else if (lca1 == x) return 1;
        else if (lca1) return 0;
        else {
            r = x;
            return 1;
        }
    }
    return 0;
}
LL gt_ans (int x) {
    int lca1 = Lca (x , l) , lca2 = Lca (x , r);
    LL ans1 , ans2;
    if (r) {
        ans1 = sz[l] , ans2 = sz[r];
        if (lca1 == x || lca2 == x) return 0;
        else if (lca1 == l) ans1 -= sz[x];
        else if (lca2 == r) ans2 -= sz[x];
    }
    else {
        ans1 = sz[l] , ans2 = sz[r] - sp;
        if (lca1 == x || lca2 == x) return 0;
        else if (lca1 == l) ans1 -= sz[x];
        else if (lca1 == 0) ans2 -= sz[x];
    }
    return ans1 * ans2;
}
int main () {
    tw[0] = 1ll;
    fu (i , 1 , 25) tw[i] = 1ll * tw[i - 1] * 2;
    int T , u , v;
    scanf ("%d" , &T);
    while (T --) {
        scanf ("%d" , &n);
        cnt = 0;
        fu (i , 0 , n) ans[i] = hd[i] = fa[i] = 0;
        fu (i , 0 , n) 
            fu (j , 0 , 25) 
                f[i][j] = -1;
        fu (i , 1 , n - 1) {
            scanf ("%d%d" , &u , &v);
            add (u , v) , add (v , u);
        }
        gt_f ();
        pre_ans ();
        for (int i = hd[0] ; i ; i = e[i].nt) {
            if (Lca (e[i].to , 1) == e[i].to) {
                sp = sz[e[i].to];
                break;
            }
        }
        l = r = 0;
        fu (i , 2 , n) {
            if (!add (i - 1)) break;
            if (i == n) {
                ans[i] = 1;
                break;
            }
            ans[i] = gt_ans (i);
        }
        fu (i , 0 , n)
            printf ("%lld " , ans[i]);
        printf ("\n");
    }
    return 0;
}

标签:sz,return,int,lca1,Tree,else,lca,MEX
From: https://www.cnblogs.com/2020fengziyang/p/17762246.html

相关文章

  • CF963B Destruction of a Tree 题解
    CF963BDestructionofaTree题解  洛谷题目链接  这里提供一个较为朴素的DP想法。题意简述  给定一棵树,节点个数不超过\(2\times10^5\),每次可以删掉度数为偶数的点。问最后能不能删完;能删完给出删除方案。思路分析  首先可以随便选一个点作为根。  其次,......
  • 动态树(Link-Cut Tree)
    算法思想动态树算法用于解决一类树上问题,涉及树边的连接和断开,并借助splay实现高效维护树上路径信息。算法细节见YangZhe的论文代码实现P3690【模板】动态树(LCT)#include<bits/stdc++.h>#definelllonglong#defineilinlineusingnamespacestd;intread(){in......
  • [gym103860D]Tree Partition
    D-TreePartition考虑将树转换到一个序列上,钦定\(1\)为根节点,\(1\)的父亲为\(0\),在序列上,孩子向父亲连边然后考虑设\(dp\)状态\(dp[i][j]\)表示前\(i\)个点,分成\(j\)段的方案数,那么\(dp[i][j]\)从\(dp[k][j-1]\)转移过来要满足以下条件之一:点\(i\)的后向边\((a,b)\)满足\(a\l......
  • JavaSE---SortedSet(TreeSet)
    SortedSet概述A{@linkSet}thatfurtherprovidesatotalorderingonitselements.提供元素排序的set;Theelementsareorderedusingtheir{@linkplainComparablenatural ordering},orbya{@linkComparator}typicallyprovidedatsortedsetcre......
  • 模型视图简介、QListWidget、QTreeWidget、QTableWidget、QStringListModel、QFileSys
    一、模型视图简介   有时,我们的系统需要显示大量数据,比如从数据库中读取数据,以自己的方式显示在自己的应用程序的界面中。早期的Qt要实现这个功能,需要定义一个组件,在这个组件中保存一个数据对象,比如一个列表。我们对这个列表进行查找、插入等的操作,或者把修改的地方写回,然后......
  • TreeAPI 递归和非递归遍历
    只包含递归和非递归遍历#include<stdio.h>#include<stdlib.h>#defineMaxSize20typedefstructnode{intdata;structnode*lchild,*rchild;}TreeNode,*Tree;typedefTreeNode*Elem;//方便修改栈中元素类型typedefstruct{Elemdata......
  • sourcetree界面卡死,改用命令行提交
    在github上创建了空仓库https://github.com/wantnon/UE-Yang-426,并clone到了本地(C:/GitHub2/UE-Yang-426),然后把自己修改过的ue4.26源码拷贝到这个路径,用sourcetree提交,结果由于一次性添加文件太多,“暂存所有”这一步sourcetree界面卡死,所以只能考虑用命令行提交了,问gpt4:于是打......
  • 题解 CF457F 【An easy problem about trees】
    尝试理解,感谢cz_xuyixuan的题解。算作是很多情况的补充说明。我们不妨先二分答案,将\(\gemid\)的设为\(1\),\(<mid\)的设为\(0\),于是问题转化为了权值均为\(0/1\)的版本。我们称一棵树的大小为其非叶节点数。我们称一棵大小为奇数的树为奇树,大小为偶数的树为偶树。对......
  • CF1878G wxhtzdy ORO Tree
    CF1878GwxhtzdyOROTree设\(f(x,y)\)表示树上\(x\)到\(y\)简单路径上的点权或和中\(1\)的个数。有一个性质:选取的\(z\)节点一定满足它比它左边的点(\(l\))或者右边的点(\(r\))的贡献至少要多一位,即\(f(x,l)<f(x,z)\)或\(f(y,r)<f(y,z)\),有了这个性质,问题就简单很多......
  • gitHub项目显示tree结构方便查阅Octotree和github中文化Tampermonkey
    1.google,安装Octotree插件,这个自行搜索,安装完成2.打开项目会出现这样的界面,安装https://blog.csdn.net/Mango_Bin/article/details/111612142,这里面链接地址去设置 1.Tampermonkey,在github中搜索github-chinese,找到相应的仓库 2.往下划拉点击Tampermonkey去安装,安装完......