• CF1508E Tree Calendar


    一、题目

    点此看题

    二、解法

    首先把操作转成人话,也就是第 (i) 轮我们选择 ( t dfs)(i),把它沿某条路径转到叶子处。我们要思考的是这条路径有什么性质,整个旋转过程又有什么性质?

    性质1:整个旋转过程不改变任意节点儿子 ( t dfs) 序的偏序关系。

    可以归纳证明,考虑旋转操作之前儿子序列满足 (a_{s_1}<a_{s_2}...<a_{s_k}),那么我们找到第一个满足 (a_u<a_{s_x})(s_x) 旋转,那么旋转之后满足 (a_{s_1}...<a_{s_{x-1}}<a_u<a_{s_{x+1}}...<a_k),可见偏序关系并不改变。

    性质2:整个旋转过程可以看成把 ( t dfs) 序变成 ( t exit) 序的过程。

    我们可以发现待旋转的 ( t dfs) 序最小值总是出现在根上,首先会把最小值转到叶子上,然后沿着旋转路径回溯之后再进入新的子树,再把次小值转下去,以次类推(...)(真的解释不清楚啊)

    根据性质 (1) 可以发现一个有趣的东西,既然偏序关系不会改变,那么题目其实间接的告诉了你偏序关系,所以我们可以唯一得到一个初始 ( t dfs),问题转化成判断这个初始状态是否能变化到末状态。

    为了方便我们找到正在旋转的点 (x)(它的 ( t dfs) 序是 (a_1-1)),然后撤回它的旋转,这样得到的状态是若干个点已经完成旋转的状态,那么整棵树的 ( t dfs) 性质就没被打乱。然后我们对于每个点判断是否合法:

    • 对于前 (x-1) 个完成旋转的点,判断它们是否与 ( t exit) 序的位置相对应。
    • 对于第 (x) 个点,旋转前它是对应 ( t exit) 节点的祖先。
    • 对于 (x) 以后的点,判断整体 ( t dfs) 序的偏序关系是否对应。

    旋转此时就是暴力撤回的次数 (+)(x-1) 个点的 ( t exit) 序深度。

    时间复杂度 (O(n)),如果 (a_1=1) 相当于没有旋转直接判等即可。

    三、总结

    初末状态的题找不变量!

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    const int M = 300005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,cur,cnt,Ind,a[M],bp[M];long long ans;
    int fa[M],np[M],ep[M],ex[M],in[M],dep[M];
    vector<int> G[M],g[M];
    int cmp(int x,int y)
    {
    	return a[x]<a[y];
    }
    void build(int u,int p)
    {
    	for(auto v:G[u])
    	{
    		if(v==p) continue;
    		dep[v]=dep[u]+1;fa[v]=u;
    		build(v,u);
    		g[u].push_back(v);
    	}
    	sort(g[u].begin(),g[u].end(),cmp);
    }
    void dfs1(int u)//get exist order
    {
    	for(auto v:g[u]) dfs1(v);
    	ex[u]=++cnt;ep[cnt]=u;
    }
    void dfs2(int u)
    {
    	if(a[u]<cur) return ;
    	in[u]=++Ind;bp[Ind]=u;
    	for(auto v:g[u]) dfs2(v);
    }
    void dfs3(int u)//get dfn order
    {
    	in[u]=++Ind;
    	for(auto v:g[u]) dfs3(v);
    }
    void rotate(int u)//rotate it to root
    {
    	if(u==1) return ;
    	ans++;
    	swap(a[u],a[fa[u]]);
    	rotate(fa[u]);
    }
    int find(int u,int v)
    {
    	if(u==v) return 1;
    	if(u==1) return 0;
    	return find(fa[u],v);
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read(),np[a[i]]=i;
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		G[u].push_back(v);
    		G[v].push_back(u);
    	}
    	build(1,0);
    	dfs1(1);
    	if(a[1]==1)//haven't move
    	{
    		dfs3(1);
    		for(int i=1;i<=n;i++)
    			if(in[i]!=a[i])
    			{
    				puts("NO");
    				return 0;
    			}
    		puts("YES
    0");
    		for(int i=1;i<=n;i++)
    			printf("%d ",a[i]);
    		puts("");
    		return 0;
    	}
    	cur=a[1]-1;
    	if(!find(ep[cur],np[cur]))
    	{
    		puts("NO");
    		return 0;
    	}
    	rotate(np[cur]);
    	for(int i=1;i<=n;i++) np[a[i]]=i;
    	for(int i=1;i<cur;i++)
    		if(np[i]!=ep[i])
    		{
    			puts("NO");
    			return 0;
    		}
    	Ind=cur-1;
    	dfs2(1);
    	for(int i=cur;i<=n;i++)
    		if(bp[i]!=np[i])
    		{
    			puts("NO");
    			return 0;
    		}
    	puts("YES");
    	for(int i=1;i<cur;i++)
    		ans+=dep[np[i]];
    	printf("%lld
    ",ans);
    	Ind=0;
    	dfs3(1);
    	for(int i=1;i<=n;i++)
    		printf("%d ",in[i]);
    }
    
  • 相关阅读:
    eclipse注释模板__自动生成方法注释
    java HashMap--统计其中有相同value的key的个数
    java synchronized 详解
    进程间通信-共享内存
    辅导-计算机编程方面
    gnu make
    适应c++ 新特性
    tomcat服务器
    springmvc笔记
    Idea使用SVN教程
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15239183.html
Copyright © 2020-2023  润新知