【2018.11.9】监考老师 / 二叉树计数 / 排名

题目

嗯,这绝对是 $noip$ 考前最后一次欢乐赛了。

希望自己考完 $noip2018$ 不用退役吧……


 

T1

直接找总和最大的行和列相加。

样例很垃圾,最好再拍点数据。


 

T2

今天最难的一道题了吧……全场几乎都推到自闭了……

只有 $wxj$ 神爷做出来了(然而没打高精度得了 $60$)。

设 $f(i,j)$ 为一棵 $i$ 层的树(最深点的深度为 $i$),是否存在长度为 $N$ 的链(注意不用强行定为最长链)时($j=0/1$)的总方案数。

这里具体讲一下。

首先,这个转移基于每次用一个根合并两棵子树,并考虑最长链与这个根的关系。

 

看图理解。然后先考虑 $f(i,0)$ 的唯一转移方案。

当左、右子树都没有长度为 $N$ 的链时,为了不让两子树被如上合并后出现长度为 $N$ 的链,我们定转移为 $f(i,0)+=f(x,0)*f(y,0)$,其中 $0\le x,y\lt i\space and\space x+y\lt N-2$。也就是枚举两棵子树的最深深度情况,保证如上合并出新树后不会出现长度为 $N$ 的链。

然后考虑 $f(i,1)$ 的三种转移方案。

1. 当左、右子树都没有长度为 $N$ 的链时,为了让两子树被如上合并后出现长度为 $N$ 的链,转移为 $f(i,1)+=f(x,0)*f(y,0)$,其中 $0\le x,y\lt i\space and\space x+y=N-2$。也就是枚举两棵子树的最深深度情况,保证如上合并出新树后会出现长度为 $N$ 的链。

2. 当一棵子树有长度为 $N$ 的链,另一棵子树没有长度为 $N$ 的链时,方法类似,转移为 $f(i,1)+=f(x,1)*f(y,0)+f(x,0)*f(y,1)$,其中 $0\le x,y\lt i\space and\space x+y=N-2$,说明略。

3. 当两棵子树都有长度为 $N$ 的链时,方法也类似,转移为 $f(i,1)+=f(x,1)*f(y,1)$,其中 $0\le x,y\lt i\space and\space x+y=N-2$。

这样转移的特点是

 


T3

原题 $bzoj5026$。

题意强调:必须从根节点出发走到一个点,才能记这个点的排名。

这题也没难度,只要明白第一问不能朴素做就行了,即不能像第二问一样,用优先队列优先走扩展到的编号最小的点

原因是这问应该优先一直走到编号为 $1$ 的人,再优先一直走到编号为 $2$ 的人,等等,与第二问贪心方法并不同。

也就是说,有可能编号很小的点要经过从起点连出的编号很大的点 才能到达,此时我们在扩展时不一定要优先走从上次起点扩展出的编号最小的点,而要先经过编号更大的点,从而更先到达编号最小的点。这样字典序才最小。

所以第一问的做法是

第二问很水啦,大胆猜想用优先队列扩展,然后优先走当前能到达的编号最大的点。

第二问能这么做的原因是:我们可以扩展到离编号最小的点只差一条边的位置,而久久不前进,转回去搞其他路径上编号更大的点。

第一问复杂度 $O(n)$,第二问复杂度 $O(n*log(n))$。

 1 #include<bits/stdc++.h>
 2 #define mp make_pair
 3 #define N 200002
 4 using namespace std;
 5 inline int read(){
 6     int x=0; bool f=1; char c=getchar();
 7     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
 8     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
 9     if(f) return x;
10     return 0-x;
11 }
12 int n,a;
13 struct edge{
14     int v,nxt;
15 }e[N];
16 int head[N],cnt;
17 inline void add(int u,int v){e[++cnt]=(edge){v,head[u]}, head[u]=cnt;}
18 int que[N],Min[N];
19 priority_queue<int>Q;
20 int ans[N];
21 void dfs(int u){
22     Min[u]=u;
23     for(int i=head[u];i;i=e[i].nxt){
24         dfs(e[i].v);
25         Min[u]=min(Min[u],Min[e[i].v]);
26     }
27 }
28 int main(){
29     freopen("sort.in","r",stdin);
30     freopen("sort.out","w",stdout);
31     n=read();
32     int i;
33     for(i=1;i<=n;++i) a=read(), add(a,i);
34     dfs(0);
35     int u,cur=1,rnk=-1;
36     que[1]=0;
37     while(cur<=n){
38         u=que[cur];
39         //printf("%d %d\n",cur,u);
40         if(!ans[u]) ans[u]=++rnk;
41         if(u==cur) while(ans[++cur]);
42         for(i=head[u];i;i=e[i].nxt) que[Min[e[i].v]]=e[i].v;
43     }
44     printf("%d",ans[1]);
45     for(i=2;i<=n;++i) printf(" %d",ans[i]);    
46     putchar('\n');
47     
48     memset(ans,0,sizeof ans);
49     Q.push(0);
50     rnk=-1;
51     while(!Q.empty()){
52         u=Q.top(), Q.pop();
53         //printf("%d %d\n",cur,u);system("pause");
54         ans[u]=++rnk;
55         for(i=head[u];i;i=e[i].nxt) Q.push(e[i].v);
56     }
57     printf("%d",ans[1]);
58     for(i=2;i<=n;++i) printf(" %d",ans[i]);    
59     return 0;
60 }
61 /*hack数据 
62 5
63 3 5 4 5 0
64 */
View Code

 

转载于:https://www.cnblogs.com/scx2015noip-as-php/p/2018_11_9.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值