
[CERC2017]Cumulative Code
发布日期:2021-05-04 16:53:01
浏览次数:42
分类:原创文章
本文共 2859 字,大约阅读时间需要 9 分钟。
思路
好像是数论+记忆化搜索?
把子树分成有父亲(A)和无父亲(B)两种情况,这两种情况的删除顺序应该是不相同的。
首先,我们来讨论一下prufer编码(这个太长了,把他简称“P”好了)的总和。
我们把以编号 x x 为根,深度为 的P的和记为 fx(k) f x ( k ) 。设 fx(k)=akx+bk+ck(x div 2) f x ( k ) = a k x + b k + c k ( x d i v 2 ) ,那么:
做到这里,我们发现:原来 ak,bk,ck a k , b k , c k 都是可以递归的!
类似的,我们记录 gx(k,i) g x ( k , i ) 表示 x x 为根, 层,已经删了 i i 个点的题目中要求的答案。
同样的,可以得到:
这里也可以递归来求。
但是,如果直接递归求解,时间复杂度会很高,大概是 的。
所以,我们需要一些技巧:
如果没有询问落在一个区间内,那么这个区间不去递归,而是直接返回0;
否则,记忆化一些状态。
具体来说,就是记录:
1. 深度小于 k div 2 k d i v 2 ;
2. 这个区间落在 [a,a+(m−1)d] [ a , a + ( m − 1 ) d ] 内。
记忆化的关键字在代码中讲,递归的方式也在代码中讲。
另外,答案采用多项式的方式保存,最终输出时计算一下多项式的值。
代码
#include <cstdio>#include <algorithm>const int maxm=15;struct data//保存答案:ax+b+c(x div 2){ long long a,b,c; data(long long a_=0,long long b_=0,long long c_=0) { a=a_; b=b_; c=c_; }};data res[maxm+1][1<<maxm];int lm[maxm+1][1<<maxm],nowcase;//lm记录最后一次记忆化是哪一次询问,res记录记忆化结果inline int addleft(data& par,data son)//par加上son的答案,其中son是par的左儿子{ par.a+=2*son.a+son.c; par.b+=son.b; return 0;}inline int addright(data& par,data son)//par加上son的答案,其中son是par的右儿子{ par.a+=2*son.a+son.c; par.b+=son.a+son.b; return 0;}data search(int deep,int nextq,int m,int d,int hasparent)/* *这里: *deep是这个点的深度 *nextq代表这个区间的左端点 *记忆化的key值是(deep,nextq) *m是最小能满足nextq+m*d大于(1<<deep)-1的m(大概吧) *d是题目中的 *hasparent决定了是A情况还是B情况 */{ if(deep==1) { data ans(0,0,1); return ans; } int size=(1<<deep)-1,mem=(deep<=maxm)&&(hasparent)&&(nextq+m*d>=size); if(mem&&(lm[deep][nextq]==nowcase))//如果在这次询问中被记忆化了,直接返回值 { return res[deep][nextq]; } data sol(0,0,0);//记录答案 int nownext=nextq,sonsize=(1<<(deep-1))-1; if((nownext<sonsize)&&(m>0))//如果这个左子树有对答案产生贡献的点 { int deltam=std::min(m,(sonsize-nownext-1)/d+1);//求m的变化量,也就是上面说的m值 addleft(sol,search(deep-1,nownext,deltam,d,1));//将左边的答案加入这个的答案 m-=deltam; nownext+=deltam*d;//防止nownext到达负数 } nownext-=sonsize; if(!hasparent)//如果时B类型的子树,删完左边后需要删根,然后删右边 { if((!nownext)&&(m>0))//如果这个点对答案产生贡献,则计算答案 { nownext+=d; --m; sol.a+=2; ++sol.b; } --nownext; } if((nownext<sonsize)&&(m>0))//如果右子树有对答案产生贡献的点 { int deltam=std::min(m,(sonsize-nownext-1)/d+1);//这一部分和左子树基本相同 addright(sol,search(deep-1,nownext,deltam,d,hasparent)); m-=deltam; nownext+=deltam*d; } nownext-=sonsize; if(hasparent)//A类型的子树,需要先计算完左右子树,再计算根 { if(m>0) //如果可以对答案产生贡献,就增加答案 //这里如果m>0,代表nextq一定为0了,否则m还可以继续减小至0,不符合“最小”的条件 { ++sol.c; } } if(mem)//记忆化一下答案 { lm[deep][nextq]=nowcase; res[deep][nextq]=sol; } return sol;}int n,q,a,m,d;int main(){ scanf("%d%d",&n,&q); while(q--) { ++nowcase; scanf("%d%d%d",&a,&d,&m); data ans=search(n,a-1,m,d,0);//计算答案多项式,这里a-1是由于根节点的编号为1 printf("%lld\n",ans.a+ans.b);//计算x为1时的答案多项式的值 } return 0;}
发表评论
最新留言
哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年03月26日 09时28分21秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
快速指数算法
2021-05-09
python去除字符串中的特殊字符(爬虫存储数据时会遇到不能作为文件名的字符串)
2021-05-09
SpringCloud微服务(03):Hystrix组件,实现服务熔断
2021-05-09
Spring 框架基础(01):核心组件总结,基础环境搭建
2021-05-09
Cassandra数据建模
2021-05-09
Internet Explorer 10 专题上线
2021-05-09
云计算之路-阿里云上:0:25~0:40网络存储故障造成网站不能正常访问
2021-05-09
网站故障公告1:使用阿里云RDS之后一个让人欲哭无泪的下午
2021-05-09
上周热点回顾(6.3-6.9)
2021-05-09
上周热点回顾(8.12-8.18)
2021-05-09
【故障公告】升级阿里云 RDS SQL Server 实例故障经过
2021-05-09
蹒跚来迟:新版博客后台上线公测
2021-05-09
[网站公告]11月26日00:00-04:00阿里云RDS升级
2021-05-09
[网站公告]又拍云API故障造成图片无法上传(已恢复)
2021-05-09
云计算之路-阿里云上:“黑色30秒”走了,“黑色1秒”来了,真相也许大白了
2021-05-09
上周热点回顾(6.9-6.15)
2021-05-09
上周热点回顾(10.20-10.26)
2021-05-09
上周热点回顾(2.16-2.22)
2021-05-09
上周热点回顾(3.2-3.8)
2021-05-09
.NET跨平台之旅:借助ASP.NET 5 Beta5的新特性显示CLR与操作系统信息
2021-05-09