CF1580E Tree Calendar
作者:互联网
前言
被这道题虐暴了……
正文
题目大意:有一棵 \(n\) 个节点,以 \(1\) 为根的树,你求出它的 dfs 序 \((a_1,...,a_n)\),每次操作可以:
- 选择最小的 \((a_u,a_v)\),满足 \(u\) 与 \(v\) 有有向边相连且 \(a_u<a_v\),并交换 \(a_u,a_v\)。
现在知道 \(k\) 次操作以后的 \(a_i\),请求出 \(k\) 和初始的 dfs 序,或判断无解。
数据范围:\(n\leq 3\cdot 10^5\)
高妙的一道思维题。
你需要观察出来一些性质:
性质1:子节点相对大小关系不会改变。
这是最强的性质,这个的证明也很容易。
假设 \(a_u\) 的子节点从小到大排序后为 \((v_1,...,v_k)\),则如果 \(a_u\) 和 \(a_{v_p}\) 交换必然满足 \(a_u>a_{v_{p-1}}\) 且 \(a_u<a_{v_{p+1}}\)。
所以子节点相对大小关系不变,我们可以直接把原树的 dfs 序构造出来。
性质2:如果 \(k\) 存在,则 \(k\) 步后的 dfs 序为原来的 exit 序。
这个也很好证明。
反证法。假设它不选原来的 exit 序而选了别的,肯定会变得更劣。(因为原来 dfs 序就是每步都选最小的)
有了这两个性质以后,就可以考虑怎样判断可行,可行就需要满足:
- \(1\) 到 \(i-1\) 要是 exit 序。
- \(i+1\) 到 \(n\) 要按顺序从上到下排列。
- \(i\) 得跑对位置。
最后 \(k\) 就是所有 \(1\) 到 \(i-1\) 的点的深度之和。
#include <bits/stdc++.h>
#define sz(x) (int)(x.size())
using namespace std;
const int mod=1e9+7,Base=233,inf=0x3f3f3f3f;
const long long INF=0x3f3f3f3f3f3f3f3f;
template<typename T>inline void chmax(T &a, T b){a=max(a,b);}
template<typename T>inline void chmin(T &a, T b){a=min(a,b);}
inline void trans(int &a,int b){a+=b;if(a>mod)a-=mod;}
const int maxn=3e5+5;
int n,a[maxn],dfnin[maxn],dfnout[maxn],fa[maxn],dep[maxn],dfnin_tim=0,dfnout_tim=0;
vector<int> g[maxn];
int vv[maxn],pos[maxn];
void dfs(int u)
{
dfnin[u]=++dfnin_tim;
vector<pair<int,int>> tmp;
for(int i=0;i<sz(g[u]);i++)
{
int v=g[u][i];
fa[v]=u;
dep[v]=dep[u]+1;
tmp.push_back(make_pair(a[v],v));
}
sort(tmp.begin(),tmp.end());
for(int i=0;i<sz(tmp);i++)
dfs(tmp[i].second);
dfnout[u]=++dfnout_tim;
vv[dfnout_tim]=u;
}
bool anc(int x,int y)
{
if(x==y)
return true;
if(x==1)
return false;
return anc(fa[x],y);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
pos[a[i]]=i;
}
int u,v;
for(int i=1;i<n;i++)
{
cin>>u>>v;
g[u].push_back(v);
}
dfs(1);
if(a[1]==1)
{
for(int i=1;i<=n;i++)
if(a[i]!=dfnin[i])
{
cout<<"NO\n";
return 0;
}
cout<<"YES\n0\n";
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
cout<<"\n";
return 0;
}
int push_val=a[1]-1,push_pos;
for(int i=1;i<=n;i++)
if(a[i]==push_val)
{
push_pos=i;
break;
}
for(int i=1;i<=n;i++)
{
if(a[i]<push_val&&a[i]!=dfnout[i])
{
cout<<"NO\n";
return 0;
}
}
if(!anc(vv[push_val],pos[push_val]))
{
cout<<"NO\n";
return 0;
}
long long res=0;
while(push_pos!=1)
{
res++;
swap(a[push_pos],a[fa[push_pos]]);
push_pos=fa[push_pos];
}
for(int i=1;i<=n;i++)
{
if(a[i]>=push_val&&a[fa[i]]>a[i])
{
cout<<"NO\n";
return 0;
}
}
cout<<"YES\n";
for(int i=1;i<=n;i++)
if(a[i]<push_val)
res+=dep[i];
cout<<res<<"\n";
for(int i=1;i<=n;i++)
cout<<dfnin[i]<<" ";
cout<<"\n";
return 0;
}
标签:dfnin,CF1580E,int,void,Tree,dfs,maxn,Calendar,节点 来源: https://www.cnblogs.com/Jerry-Jiang/p/16576261.html