CF 1529E. Trees of Tranquillity
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF 1529E. Trees of Tranquillity相关的知识,希望对你有一定的参考价值。
CF 1529E. Trees of Tranquillity
题意:
有A1,A2两棵树,根是1,编号都是1~n,先制作图A3,如果两个点的x和y同时满足以下两个条件则连边,
1.在树A中x是y的祖先或者y是x的祖先
2.在树B中x和y谁都不是谁的祖先
求A3的最大的团集的大小
团:图G的一个完全子图
题目A1和A2的输入方式为:
a2,a3…an, ai是树的顶点i的父亲节点(1<= ai <i)
题解:
参考题解:
文章1
文章2
我们需要将题意转化:
满足最大团的点是什么样的?团要求任意两点都有连线,也就是最大团中所有点同时满足题目说的两个条件,因此这些点在A树上是一条的(没有分支)。在B树上体现为彼此不是父亲节点,我们引入dfs序,发现所有点的dfs序没有交集。
对于本题的输入还有一个特殊性质:ai <i,说明A树和B树从根节点出发的链,一定是一个单调递增的序列。针对B树的dfs序区间,就会有:序号较小的点的区间,要么包含序号较大的点的区间,要么与其不相交
对于两个点的dfs序区间,要么没有交集(不是父子关系),要么存在包含(父子关系),且左端点与右端点是对应的,父亲系节点的区间包含儿子节点的区间,因此对于一个互相包含的区间,我们要保留较小的(这样才能存下更多不相交的区间),对应到树上,相当于 当一个点的儿孙可选时该点不选最优
现在我们从第一棵树的根开始dfs,每到一个点就往某数据结构中加入自己的区间:
1.如果被数据结构中原先的更大区间包含,那就删除大区间,加入小区间
2.如果包含了原有的至少一个小区间,那就不加
3.否则就加进去,答案+1
回溯时,数据结构要退回原来的状态
这个某数据结构可以用set,线段树等等
线段树具体实现过程:
先求树2的dfs序,然后对树1从根节点开始,查看当前点u的区间内是否有区间,如果没有直接插入,如果有,一定是比自己大的区间,所以删除原大区间,加入新区间。记录当前答案最大值,对u的儿子继续查找。回溯时逆向操作
代码:
线段树代码:
代码里有注释
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
#include<unordered_map>
#define lowbit(x) x&-x
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)
{
T f=1;x=0;
char ch=getchar();
while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
x*=f;
}
template<typename T>
inline void write(T x)
{
if(x<0){x=~(x-1);putchar('-');}
if(x>9)write(x/10);
putchar(x%10+'0');
}
const int inf=0x3f3f3f3f;
const int N=1e6+100;
vector<int>a[N],b[N];
int L[N],R[N],dfn,sum,ans;
struct Node {
int l,r,mmax,lazy;
}tree[N<<2];
void pushup(int k) {
tree[k].mmax=max(tree[k<<1].mmax,tree[k<<1|1].mmax);
}
void pushdown(int k) {
if(tree[k].lazy!=-1) {
int lz=tree[k].lazy;
tree[k].lazy=-1;
tree[k<<1].mmax=tree[k<<1|1].mmax=lz;
tree[k<<1].lazy=tree[k<<1|1].lazy=lz;
}
}
void build(int k,int l,int r) {
tree[k]={l,r,0,-1};
if(l==r) {
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void update(int k,int l,int r,int val) {
if(tree[k].l>r||tree[k].r<l) {
return;
}
if(tree[k].l>=l&&tree[k].r<=r) {
tree[k].mmax=tree[k].lazy=val;
return;
}
pushdown(k);
update(k<<1,l,r,val);
update(k<<1|1,l,r,val);
pushup(k);
}
int query(int k,int l,int r) {
if(tree[k].l>r||tree[k].r<l) {
return 0;
}
if(tree[k].l>=l&&tree[k].r<=r) {
return tree[k].mmax;
}
pushdown(k);
return max(query(k<<1,l,r),query(k<<1|1,l,r));
}
void dfs1(int u) {//求树2的dfs序
L[u]=++dfn;
for(auto v:b[u]) {
dfs1(v);
}
R[u]=dfn;
}
void dfs2(int u) {
int mmax=query(1,L[u],R[u]);
if(!mmax) {//如果没有区间
update(1,L[u],R[u],u);
sum++;
} else {//存在区间,且区间一定比自己大
update(1,L[mmax],R[mmax],0);//删除原本区间
update(1,L[u],R[u],u);//加入新区间
}
ans=max(ans,sum);
for(auto v:a[u]) {
dfs2(v);//对u的儿子节点继续查找
}
//回溯操作
if(!mmax) {
update(1,L[u],R[u],0);
sum--;
} else {
update(1,L[u],R[u],0);
update(1,L[mmax],R[mmax],mmax);
}
}
int main()
{
int w;
cin>>w;
while(w--) {
int n;
read(n);
dfn=0;
for(int i=1;i<=n;i++) {
a[i].clear();
b[i].clear();
}
for(int i=2;i<=n;i++) {
int fa;
read(fa);
a[fa].push_back(i);
}
for(int i=2;i<=n;i++) {
int fa;
read(fa);
b[fa].push_back(i);
}
dfn=ans=sum=0;
build(1,1,n);
dfs1(1);
dfs2(1);
cout<<ans<<endl;
}
return 0;
}
利用set实现
#include<set>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 300005
#define ENDL putchar('\\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
#define SI set<int>::iterator
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
int n,m,i,j,s,o,k;
vector<int> g0[MAXN];
int L[MAXN],R[MAXN],lR[MAXN],tim;//lR[]数组是为L找到唯一的R
void dfs0(int x,int ff) {//求 树2的dfs序
L[x] = ++ tim;
for(int i = 0;i < (int)g0[x].size();i ++) {
if(g0[x][i] != ff)
dfs0(g0[x][i],x);
}
R[x] = tim;
lR[L[x]] = R[x];
return ;
}
vector<int> g[MAXN];
int d[MAXN],dfn[MAXN],rr[MAXN],cnt,ans;
set<int> st;
void dfs(int x,int ff) {
int ad = 0;
if(st.empty()) st.insert(L[x]);//如果此时为空,直接插入
else {
SI i = st.lower_bound(L[x]);//查找是否已经有区间
if(i != st.begin()) //发现有区间
{
i --;
if(lR[*i] >= R[x])//存在更大区间包含
{
ad = *i;
st.erase(ad);//删除大区间
st.insert(L[x]);//加入小区间
}
else
{
i ++;
if(i == st.end() || *i > R[x]) //里面没有小区间,加入的区间不会相交
st.insert(L[x]);
}
}
else if(i == st.end() || *i > R[x])
st.insert(L[x]);//发现没区间
}
ans = max(ans,(int)st.size());
for(int i = 0;i < (int)g[x].size();i ++) {
if(g[x][i] != ff)
dfs(g[x][i],x);
}
//回溯操作
if(st.find(L[x]) != st.end()) st.erase(L[x]);
if(ad) st.insert(ad);
return ;
}
int main() {
int T = read();
while(T --) {
n = read();
tim = 0; cnt = 0;
st.clear();
for(int i = 1;i <= n;i ++) {
g0[i].clear();
g[i].clear();
lR[i] = 0;
}
for(int i = 2;i <= n;i ++) {
s = read();
g[s].push_back(i);
}
for(int i = 2;i <= n;i ++) {
s = read();
g0[s].push_back(i);
}
dfs0(1,0);
ans = 0;
dfs(1,0以上是关于CF 1529E. Trees of Tranquillity的主要内容,如果未能解决你的问题,请参考以下文章
[CF1528C]Trees of Tranquillity