首页 > 其他分享 >树上问题学习

树上问题学习

时间:2024-09-27 15:26:05浏览次数:7  
标签:问题 得分 mathscr 替换 学习 text 树上 节点 size

T1

题面

有一个 \(n\) 个节点的树,根节点为 \(1\) ,令叶子节点数为 \(m\) ,叶子节点的权值为一个 \(1\) 到 \(m\) 的排列 。Alice和Bob在树上玩游戏,两人从根节点开始,Alice先手的轮流的行走 \(u\to\text{son}(u)\) 的路径直到抵达叶子节点。叶子的权值为本次游戏的得分。Alice希望最大化得分,Bob希望最小化得分。在某种叶子权值的排列下,得分的最大值和最小值分别是多少。
数据范围:
\(1\le n\le 2\times 10^5\)

题解

容易发现两种情况对称,先考虑求得分的最大值。
状态定义:
定义状态 \(f_{u,0/1}\) , \(f_{u,0}\) 表示在 \(u\) 节点的人希望最大化得分时能得到子树第 \(f_{u,0}\) 大的权值; \(f_{u,1}\) 表示在 \(u\) 节点的人希望最小化得分时能得到子树第 \(f_{u,1}\) 大的权值
边界条件:
对于所有叶子节点有 \(f_{u,0}=f_{u,1}=1\) 。
状态转移方程:
以下令 \(v\in\text{son}(u)\) 。
考虑 \(f_{u,0}\) 的转移,因为要令得分最大,所以最优策略是把 \(f_{v,1}\) 中的最小值 \(x\) 取出来,然后把所有权值中最大的 \(x\) 个点全部放入使 \(f_{v,1}\) 最小的 \(v\) 的子树中,此时就可以直接走到节点 \(v\) 并取到最大值,所以有:

\[f_{u,0}=\min_{v\in\text{son}(u)} f_{v,1} \]

考虑 \(f_{u,1}\) 的转移,因为要令得分最大,所以最优策略是吧 \(f_{v,0}\) 中所有的数和 \(x\) 算出来,然后把所有权值中最大的 \(x\) 个点在每个 \(v\) 的子树中放 \(f_{v,0}\) 个,这样可以使 \(u\) 在行动时无论如何走都只能使最小值 \(\ge\) 第 \(x\) 大的数,所以有:

\[f_{u,1}=\sum_{v\in\text{son}(u)} f_{v,0} \]

最终答案:
最终得分的最大值为 \(m-f_{1,0}+1\) 。考虑得分的最小值相当于所有权值反转 \(x\leftarrow m-x+1\) 后得分的最大值且先手是想要得分最小的人,得分为 \(m-f_{1,1}+1\) ,再反转回去可以得到得分的最小值 \(f_{1,1}\) 。
总复杂度 \(\Theta(n)\)

代码

#include<cstdio>
#define Min(x,y) ((y)<(x)&&((x)=(y)))
const int N=200005,INF=0x3f3f3f3f;
int n,head[N],pedge,f[N][2],cnt;
struct Edge{
    int to,next;
}edge[N<<1];
inline void Ins(int u,int v){
    edge[++pedge]={v,head[u]},head[u]=pedge;
}
inline void ins(int u,int v){
    Ins(u,v),Ins(v,u);
}
void dfs(int u,int father){
    bool flag=1;
    f[u][0]=INF,f[u][1]=0;
    for(int e=head[u];e;e=edge[e].next){
        int v=edge[e].to;
        if(v==father)continue;
        dfs(v,u),flag=0;
        Min(f[u][0],f[v][1]),f[u][1]+=f[v][0];
    }
    if(flag)f[u][0]=f[u][1]=1,cnt++;
}
int main(){
    scanf("%d",&n);
    for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),ins(u,v);
    dfs(1,0),printf("%d %d\n",cnt-f[1][0]+1,f[1][1]);
    return 0;
}

T2

题面

接下来的树指非空、有根、区分左右孩子的二叉树。
定义:

  1. 称 \(T\) 能单步替换成为 \(T'\) 表示存在 \(T''\) 和 \(T\) 的一个叶子节点 \(u\) 使得将 \(u\) 替换成 \(T''\) 后 \(T=T'\) ,记做 \(T\to T'\) 。
  2. 称 \(T\) 能替换成为 \(T'\) 表示存在正整数 \(n\) ,使得 \(T=T_1\to T_2\to\cdots\to T_n=T'\) ,记做 \(T\to^*T'\) 。
  3. 定义 \(\text{grow}(T)=\{T'|T\to T'\}\) 。
  4. 对森林 \(\mathscr T=\{T_1,T_2,\dots,T_n\}\) ,定义 \(\text{grow}(\mathscr T)=\bigcup_{T\in\mathscr T}\text{grow}(T)\) 。

有 \(N\) 组询问,每组询问有 \(m\) 个树组成森林 \(\mathscr T=\{T_1,T_2,\dots,T_m\}\) ,判断是否有有限个树 \(T\) 满足 \(T\notin\text{grow}(\mathscr T)\) 。
数据范围:
令每个树的大小为 \(n\) , \(N\le 100\) ,\(1\le\sum n,\sum m\le 2\times 10^6\)

题解

令森林 \(\mathscr T\) 中深度最大的树的深度为 \(d_{\max}\) 。令不存在的节点的 \(size\) 为 \(0\) 。
定理 \(1\) :
存在深度为 \(d>d_{\max}\) 的树不能被替换得到当且仅当存在深度为 \(d-1\) 的树不能被替换得到。
反证,所有的深度为 \(d-1\) 的树能被替换得到当且仅当所有深度为 \(d\) 的树能被替换得到。所以只用考虑深度为 \(d_{\max}\) 的树是否能被替换得到。如果全部满足,那么只有限个树 \(T\) 满足 \(T\notin\text{grow}(\mathscr T)\) ,因为只有深度 \(<d_{\max}\) 的树才可能不能被替换得到。
定理 \(2\) :
所有深度为 \(d_{\max}\) 的树不能被替换得到当且仅当所有深度为 \(d_{\max}\) 的好的树不能被替换得到。其中好的树定义为对于树上的每一个节点 \(u\) ,有 \(\min(size_{ls_u},size_{rs_u})\le 1\) 。
对深度为 \(d_{\max}\) 的树,选择一条从根到叶子的长度为 \(d_{\max}\) 的链,可以构造一颗好树同样拥有这条链,且好树的每个点是否有不在链上的儿子取决于原树是否有不在链上的儿子。这样子构造的好树可以通过替换得到原树。这样只需要判断是否能替换得到所有的深度为 \(d_{\max}\) 的好树。
定理 \(3\) :
不是好树的树不能替换成好树。
对任意不是好树的树,存在节点 \(u\) 使得 \(\min(size_{ls_u},size_{rs_u})>1\) 替换后 \(size\) 一定不小于原来的值,新树仍然不是好树。这样子就只用考虑 \(\mathscr T\) 中所有是好树的树,删去不是好树的树。
定理 \(4\) :
任何一个深度为 \(d\) 的好树,可以对应一个或两个长度为 \(d-1\) 的 \(4\) 进制数。
对于任何一个好树的链上的非叶子节点 \(u\) 可以对应四种状态:

  1. \(size_{ls_u}=0\) ,\(u\) 对应数位上的数为 \(0\)。
  2. \(size_{rs_u}=0\) ,\(u\) 对应数位上的数为 \(1\)。
  3. \(size_{ls_u}=1\) ,\(u\) 对应数位上的数为 \(2\)。
  4. \(size_{rs_u}=1\) ,\(u\) 对应数位上的数为 \(3\)。

这条链上的每个节点依次替换成四种数字,再建立 trie 树,就可以将问题简化。若有两个深度为 \(d\) 的儿子,则可能同时对应 \(2\) 和 \(3\) ,整个好树可能对应两个四进制树。
定理 \(5\) :
对任意一个 \(\mathscr T\) 中的好树 \(T\) , 如果 \(T'\) 对应的四进制数的一个前缀是 \(T\) ,\(T'\) 能被替换得到。
这样子就可以将需要在 trie 树上建立的点的个数从 \(\text O(4^{d_{\max}})\) 变成 \(\text O(\sum n)\) 个了。
根据定理得到一个做法:将 \(\mathscr T\) 中所有的好树取出来,转化成 \(4\) 进制建立 trie 树。一个节点中所有的子树都能被替换得到需要满足如下至少一个条件:

  1. 该节点对应的好树在 \(\mathscr T\) 中存在。
  2. 该节点的四个儿子都存在且四个节点对应的子树都能被替换得到。

时间复杂度 \(\text O(\sum n)\)

代码

#include<cstdio>
#include<cstring>
#define min(x,y) ((x)<(y)?(x):(y))
const int N=2000005;
int T,n,m,son[N][2],root,size[N],cnt,s[N][4];
bool flag[N];
bool check(int u){
    size[u]=1;
    for(int i=0;i<2;i++)if(son[u][i]){
        if(!check(son[u][i]))return 0;
        size[u]+=size[son[u][i]];
    }
    return min(size[son[u][0]],size[son[u][1]])<=1;
}
void ins(int&x,int u){
    if(!x)x=++cnt;
    if(!son[u][0]&&!son[u][1])flag[x]=1;
    if(!son[u][0]&&son[u][1])ins(s[x][0],son[u][1]);
    if(!son[u][1]&&son[u][0])ins(s[x][1],son[u][0]);
    if(size[son[u][0]]==1&&son[u][1])ins(s[x][2],son[u][1]);
    if(size[son[u][1]]==1&&son[u][0])ins(s[x][3],son[u][0]);
}
bool solve(int u){
    if(!u)return 0;
    return flag[u]||(solve(s[u][0])&&solve(s[u][1])&&solve(s[u][2])&&solve(s[u][3]));
}
int main(){
    for(scanf("%d",&T);T--;){
        memset(flag,0,sizeof(flag)),memset(s,0,sizeof(s)),root=cnt=0;
        scanf("%d",&m);
        for(;m--;){
            scanf("%d",&n);
            for(int i=1;i<=n;i++)scanf("%d%d",son[i],son[i]+1);
            if(check(1))ins(root,1);
        }
        puts(solve(root)?"Almost Complete":"No");
    }
    return 0;
}

话说为什么我会给我们班讲题

标签:问题,得分,mathscr,替换,学习,text,树上,节点,size
From: https://www.cnblogs.com/junjunccc/p/18435812

相关文章

  • !!!史上最全深度学习环境安装包【文末附链接】
    配置深度学习环境是一个十分复杂的工程,里面牵扯到非常多的库和工具,而且这些库和工具的版本之间还有很多的依赖关系。深度学习框架(如PyTorch)和相关库(如NumPy、Pandas)有许多依赖,确保这些依赖的版本兼容性对于避免运行时错误至关重要。由于网络带宽和服务器位置的限制,一些深度......
  • 新手答疑 | 零基础该怎么学习嵌入式?嵌入式Linux学习路线是什么?嵌入式开发板推荐?
    很多初学者想要涉足嵌入式Linux开发领域,但往往在刚入门阶段,会因为初次接触到大量复杂的概念术语和深奥的技术文档感到压力重重,面对这些内容不知从何下手,感到十分迷茫,网上的内容也纷繁复杂,没有清晰的学习线路,看完后甚至更蒙圈。实际上,嵌入式Linux开发并没有想象中的那样高不可攀,关键......
  • C++顺序结构(2)学习任务
    在坚果云中注册免费个人云盘一、视频下载存放在规划好的文件夹中,并观看学习1、变量存储.注释2、四则运算.输入3、认识设置DEV-C++4、第一个C++程序5、头文件6、命名空间7、cout语句8、什么是变量?9、表达式和赋值语句10、计数器11、自加自减运算符12、课后题目113、......
  • Flask学习笔记(二)
    目录1.遇到的问题2.知识点2.1ORM模型2.1.1 ORM模型映射到增删改查2.1.2数据库外键2.1.3创建一个数据库表2.1.4通过外键关系查找2.1.5更新数据库结构2.2请求上下文和应用上下文2.2.1请求上下文2.2.2应用上下文3.开启Debug模式1.遇到的问题1.sqlalchemy.......
  • java的基础入门学习03——抽象类与抽象方法的使用
    文章目录前言1、抽象类1.1什么是抽象类1.2如何使用抽象类2、抽象方法2.1什么是抽象方法2.2抽象方法的使用3、运用实例前言首先在学习抽象类以及抽象方法之前,我们得先了解什么是抽象,抽象其实也被成为面向对象的第四大特征,abstract就是java中对应的关键字,抽象往......
  • Linux学习完真的能找到工作吗?
    Linux作为一种开源的操作系统,在企业和机构中得到了广泛的应用,因此需要大量的Linux专业人才。而Linux知识体系比较广泛,想要从事相关工作需要具备专业的技能,因此为了满足用人标准,不少小伙伴会选择参加培训,那么Linux培训完能找到工作吗?以下是具体内容介绍。Linux培训完能找到......
  • java的基础入门学习02-面向对象特性及使用
    文章目录前言面向对象1、什么是面向对象2、面向对象的三大特性2.1封装特性2.2继承特性2.3多态特性前言java中经常会把需要使用到的数据结构来封装成对象,而当我们这些后来希望使用前辈留下来的代码或者自己拓展功能供大家借鉴使用,面向对象是学习java中十分重要的......
  • ClkLog常见问题-埋点集成篇Sec. 2
    本篇将继续解答ClkLog使用过程中【埋点集成】阶段的常见问题。1. 【埋点集成】问:receiver数据接收是不是一定要有ssl证书?答:不是。2. 【埋点集成】问:接收服务地址从哪里获取?答:接收服务地址参考:http(s)://{YOUR_IPORDOMAIN}/receiver/api/gp?project={clklogapp}&token={apptok......
  • 自学网络安全(黑客技术)2024年 90天学习计划
    ......
  • 基本双指针问题
    c++数据结构p2#include<iostream>#include<stdlib.h>#include<time.h>usingnamespacestd;//奇偶数调整问题//整型数组,把偶数调整到数组的左边,把奇数调整到数组的右边voidAdjustArray(intarr[],intsize){ int*p=arr; int*q=arr+size-1; //p->......