zoukankan      html  css  js  c++  java
  • 【洛谷4738】[CERC2017] Cumulative Code(Meet in Middle)

    点此看题面

    • 一棵(n)层的满二叉树,从上往下、从左往右编号,设(p)为它的(prufer)序列。
    • (q)次询问,每次给出(a,d,m),求(sum_{i=1}^mp_{a+(i-1) imes d})
    • (nle30,qle300)

    暴力搜索

    如果当前点有父节点,那么我们会先把左子树删完,再把右子树删完,最后删去当前点。

    如果当前点没有父节点,那么我们会先把左子树删完,然后就删去当前点,最后把右子树删完。

    因此可以写一个暴搜,记录当前点编号以及是否有父节点即可。

    折半思想

    由于这是一棵满二叉树,子树的形态只和深度有关,容易发现(prufer)序列的每一项都可以写成一个关于子树根节点编号的一次函数(k_ix+b_i)

    我们预处理出第(lfloorfrac n2 floor+1)层节点子树内的(prufer)序列(注意,根据是否有父节点,会分为两种)。

    每次询问时事先预处理出(k_i,b_i)(d)项的前缀和,然后在前(lfloorfrac n2 floor)层中暴搜,一旦进入第(lfloorfrac n2 floor+1)层就利用预处理出的前缀和以及当前点编号求出答案。

    代码:(O(q2^{frac n2}))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 30
    #define S 65536
    #define LL long long
    using namespace std;
    int n,a,d,m,ct,k[2][S+5],b[2][S+5];LL K[2][S+5],B[2][S+5];
    I void Init(CI ty,CI x,CI y,CI p,CI fg)//预处理
    {
    	if(p>n) return;//超出深度
    	if(fg) Init(ty,x<<1,y<<1,p+1,0),k[ty][++ct]=x<<1,b[ty][ct]=y<<1|1,Init(ty,x<<1,y<<1|1,p+1,1);//左中右
    	else Init(ty,x<<1,y<<1,p+1,0),Init(ty,x<<1,y<<1|1,p+1,0),k[ty][++ct]=x>>1,b[ty][ct]=y>>1;//左右中
    }
    I LL Calc(CI x,CI nw,CI fg)//计算子树根节点编号为x,之前访问过总点数为nw时的答案
    {
    	RI l=max(nw+1,a),r=min(nw+ct,a+(m-1)*d);l+=(a%d-l%d+d)%d,r-=(r%d-a%d+d)%d;if(l>r) return 0;//把l,r调成与a同余
    	return l-=nw,r-=nw,K[fg][r]*x+B[fg][r]-(l>d?K[fg][l-d]*x+B[fg][l-d]:0)+(!fg&&r==ct?x>>1:0);//利用隔d前缀和计算答案,特判根节点父节点的贡献
    }
    int nw;LL ans;I void dfs(CI x,CI p,CI fg)//在前n/2层暴搜
    {
    	if(p>n/2) return (void)(ans+=Calc(x,nw,fg),nw+=ct);//达到第n/2+1层,利用预处理结果计算答案
    	if(fg) dfs(x<<1,p+1,0),++nw>=a&&!((nw-a)%d)&&(nw-a)/d<m&&(ans+=x<<1|1),dfs(x<<1|1,p+1,1);//左中右
    	else dfs(x<<1,p+1,0),dfs(x<<1|1,p+1,0),++nw>=a&&!((nw-a)%d)&&(nw-a)/d<m&&(ans+=x>>1);//左右中
    }
    int main()
    {
    	RI Qt,i,j,x,y,z;scanf("%d%d",&n,&Qt),Init(0,1,0,n/2+1,0),ct=0,Init(1,1,0,n/2+1,1);W(Qt--)
    	{
    		for(scanf("%d%d%d",&a,&d,&m),i=1;i<=ct;++i)
    			for(j=0;j<=1;++j) K[j][i]=(i>d?K[j][i-d]:0)+k[j][i],B[j][i]=(i>d?B[j][i-d]:0)+b[j][i];//隔d前缀和
    		nw=ans=0,dfs(1,1,1),printf("%lld
    ",ans);
    	}return 0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    zech的神秘题库(武汉理工夜莺杯)
    回归第六题
    同余方程
    牛牛选路径(牛客)
    回归第三题
    区间dp复习提高专题
    乘法逆元(线性递推)
    回归第八题
    JAVA启动参数大全之二:非标准参数(转)
    (转)Spring Security 3.1 自定义实例之登陆
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4738.html
Copyright © 2011-2022 走看看