[BZOJ3244][NOI2013]树的计数
2015-07-12 21:26
323 查看
这题大家为什么都写O(NlogN)的算法呢?……
让本蒟蒻来写一个O(N)的吧……
首先还是对BFS序和DFS序重编号,记标好的DFS序为d[1..n]。令pos[x]为x在d[]中出现的位置,即pos[d[i]]=i。
然后还是要用到一个BFS序的分段对应一棵树的结论……然后我们考察一个分段方式的合法性:首先结点1是唯一的根必须要单独一段;其次,BFS序中一层的结点出现的顺序和DFS序中的顺序一定是相同的,因此对于任何的一段[l, r],都有pos[l]<pos[l+1]<pos[l+2]<…<pos[r];最后我们还需要考虑结点深度的约束,对于DFS序中两个相邻结点,后一个结点的深度至多比前一个结点大1。综上所述,我们得到了以下三条约束:
(1)结点1单独分为一段;
(2)对于任何一段[l, r],pos[l]<pos[l+1]<pos[l+2]<…<pos[r];
(3)记depth[i]为结点i的深度,则depth[d[i+1]]<=depth[d[i]]+1。
我们尝试转化上面3条约束。建立数组s[1..n-1],s[i]=1当且仅当结点i和结点i+1分在不同的两段,否则s[i]=0。这样进行转化之后,以上三条约束变成了如下的形式:
(1)s[1]=1;
(2)若pos[i]>pos[i+1],则s[i]=1;
(3)若d[i]<d[i+1],则Σ{s[j] | j=d[i]..d[i+1]-1}<=1。
第一条转化是容易理解的。第三条转化也容易理解,因为s[d[i]]、s[d[i]+1]、…、s[d[i+1]-1]的和就等于结点d[i]和d[i+1]的高度差。当d[i]>d[i+1]时,由于d[i]的BFS序在d[i+1]后面,所以depth[d[i+1]]<=depth[d[i]]<depth[d[i]]+1。第二条的转化是把原先的描述反过来理解形成的,也就是说若pos[i]>pos[i+1],那么结点i一定不满足约束,所以必须分为两段。可以证明这样的转化与原来的约束是等效的。
显然,1+Σs[i]就是树高。
我们考虑在O(N)时间内计算出树高的期望。约束1和约束2把一些s[i]固定为1,它们对s[]造成的影响可以非常容易地O(N)处理出来。然后考虑第三类约束,首先我们处理所有Σ{s[j] | j=d[i]..d[i+1]-1}=1的第三类约束,这类约束固定了一个区间的值(某一个s[i]=1,其余s[i]均为0)。这些值可以通过前缀和的处理技巧O(1)地打好固定标记,所以处理这些约束也是O(N)的。
最后我们剩下一些Σ{s[j] | j=d[i]..d[i+1]-1}=0的约束。由于每一个s[j]都是0,这意味着对于所有的j=l..r-1,都有pos[j]<pos[j+1](否则处理第二类约束时会使得某个s[j]=1,从而不满足Σs[j]=0)。因此,我们有pos[l]<pos[l+1]<pos[l+2]<…<pos[r]。注意到这里l=d[i]而r=d[i+1],所以pos[l]=i而pos[r]=i+1。也就是说这些剩下的约束其实都是一些形如i<i+1的恒成立的不等式,直接无视掉就好了。这样就处理完了所有的约束,最后我们还剩下一些位置没有被固定,这些位置取0或1均可,对答案的贡献就是0.5。
于是我们就可以O(N)地解决这个问题。代码异常的简洁,线段树什么的根本不用写嘛……
让本蒟蒻来写一个O(N)的吧……
首先还是对BFS序和DFS序重编号,记标好的DFS序为d[1..n]。令pos[x]为x在d[]中出现的位置,即pos[d[i]]=i。
然后还是要用到一个BFS序的分段对应一棵树的结论……然后我们考察一个分段方式的合法性:首先结点1是唯一的根必须要单独一段;其次,BFS序中一层的结点出现的顺序和DFS序中的顺序一定是相同的,因此对于任何的一段[l, r],都有pos[l]<pos[l+1]<pos[l+2]<…<pos[r];最后我们还需要考虑结点深度的约束,对于DFS序中两个相邻结点,后一个结点的深度至多比前一个结点大1。综上所述,我们得到了以下三条约束:
(1)结点1单独分为一段;
(2)对于任何一段[l, r],pos[l]<pos[l+1]<pos[l+2]<…<pos[r];
(3)记depth[i]为结点i的深度,则depth[d[i+1]]<=depth[d[i]]+1。
我们尝试转化上面3条约束。建立数组s[1..n-1],s[i]=1当且仅当结点i和结点i+1分在不同的两段,否则s[i]=0。这样进行转化之后,以上三条约束变成了如下的形式:
(1)s[1]=1;
(2)若pos[i]>pos[i+1],则s[i]=1;
(3)若d[i]<d[i+1],则Σ{s[j] | j=d[i]..d[i+1]-1}<=1。
第一条转化是容易理解的。第三条转化也容易理解,因为s[d[i]]、s[d[i]+1]、…、s[d[i+1]-1]的和就等于结点d[i]和d[i+1]的高度差。当d[i]>d[i+1]时,由于d[i]的BFS序在d[i+1]后面,所以depth[d[i+1]]<=depth[d[i]]<depth[d[i]]+1。第二条的转化是把原先的描述反过来理解形成的,也就是说若pos[i]>pos[i+1],那么结点i一定不满足约束,所以必须分为两段。可以证明这样的转化与原来的约束是等效的。
显然,1+Σs[i]就是树高。
我们考虑在O(N)时间内计算出树高的期望。约束1和约束2把一些s[i]固定为1,它们对s[]造成的影响可以非常容易地O(N)处理出来。然后考虑第三类约束,首先我们处理所有Σ{s[j] | j=d[i]..d[i+1]-1}=1的第三类约束,这类约束固定了一个区间的值(某一个s[i]=1,其余s[i]均为0)。这些值可以通过前缀和的处理技巧O(1)地打好固定标记,所以处理这些约束也是O(N)的。
最后我们剩下一些Σ{s[j] | j=d[i]..d[i+1]-1}=0的约束。由于每一个s[j]都是0,这意味着对于所有的j=l..r-1,都有pos[j]<pos[j+1](否则处理第二类约束时会使得某个s[j]=1,从而不满足Σs[j]=0)。因此,我们有pos[l]<pos[l+1]<pos[l+2]<…<pos[r]。注意到这里l=d[i]而r=d[i+1],所以pos[l]=i而pos[r]=i+1。也就是说这些剩下的约束其实都是一些形如i<i+1的恒成立的不等式,直接无视掉就好了。这样就处理完了所有的约束,最后我们还剩下一些位置没有被固定,这些位置取0或1均可,对答案的贡献就是0.5。
于是我们就可以O(N)地解决这个问题。代码异常的简洁,线段树什么的根本不用写嘛……
/************************************************************** Problem: 3244 User: IcyF Language: C++ Result: Accepted Time:216 ms Memory:6276 kb ****************************************************************/ #include <cstdlib> #include <cstdio> using namespace std; #define rep(n) for (int i = 1; i <= n; ++i) #define sum(l, r) (s[r] - s[l - 1]) const int MAXN = 200005; int n, a[MAXN], b[MAXN], c[MAXN], pos[MAXN], x[MAXN], s[MAXN], fix[MAXN]; int main() { scanf("%d", &n); rep(n) scanf("%d", a + i); rep(n) scanf("%d", b + i); rep(n) c[b[i]] = i; rep(n) a[i] = c[a[i]]; rep(n) pos[a[i]] = i; x[1] = 1, ++fix[1], --fix[2]; rep(n - 1) if (pos[i] > pos[i + 1]) x[i] = 1, ++fix[i], --fix[i + 1]; rep(n - 1) s[i] = s[i - 1] + x[i]; rep(n - 1) if (a[i] < a[i + 1]) { int t = sum(a[i], a[i + 1] - 1); if (t) { ++fix[a[i]]; --fix[a[i + 1]]; } } double ans = 0; int f = 0; rep(n - 1) { f += fix[i]; if (f) ans += double(x[i]); else ans += 0.5; } printf("%.3lf\n", ans + 0.999); printf("%.3lf\n", ans + 1.0); printf("%.3lf\n", ans + 1.001); return 0; }
相关文章推荐
- Spark中加载本地(或者hdfs)文件以及SparkContext实例的textFile使用
- 字符串(NSString)、字典(NSDictionary)、数组(NSArray)的总结
- Linux网络编程-自己动手写进程池
- 【转】关于redis.conf的参数配置
- [leedcode 53] Maximum Subarray
- git使用详解
- B/S学习总结
- 在广播接收者里面启动Activity
- Linux常用命令集合
- Nginx502,504和499错误解决方案
- springmvc使用实体参数和ServletAPI
- Nmap系列E------OSI四层扫描
- 再谈网游同步技术
- Java解惑
- Android开源项目汇总20150712更新
- Java事件处理
- Touch panel DTS 分析(MSM8994平台,Atmel 芯片)
- Nginx Location配置总结
- 深入理解去耦电容
- 如何在MFC的ListCtrl控件中不按行的上下顺序添加数据