【洛谷4738】[CERC2017] Cumulative Code(Meet in Middle)
阿新 • • 發佈:2021-06-17
- 一棵\(n\)層的滿二叉樹,從上往下、從左往右編號,設\(p\)為它的\(prufer\)序列。
- \(q\)次詢問,每次給出\(a,d,m\),求\(\sum_{i=1}^mp_{a+(i-1)\times d}\)。
- \(n\le30,q\le300\)
暴力搜尋
如果當前點有父節點,那麼我們會先把左子樹刪完,再把右子樹刪完,最後刪去當前點。
如果當前點沒有父節點,那麼我們會先把左子樹刪完,然後就刪去當前點,最後把右子樹刪完。
因此可以寫一個暴搜,記錄當前點編號以及是否有父節點即可。
折半思想
由於這是一棵滿二叉樹,子樹的形態只和深度有關,容易發現\(prufer\)序列的每一項都可以寫成一個關於子樹根節點編號的一次函式\(k_ix+b_i\)
我們預處理出第\(\lfloor\frac n2\rfloor+1\)層節點子樹內的\(prufer\)序列(注意,根據是否有父節點,會分為兩種)。
每次詢問時事先預處理出\(k_i,b_i\)隔\(d\)項的字首和,然後在前\(\lfloor\frac n2\rfloor\)層中暴搜,一旦進入第\(\lfloor\frac n2\rfloor+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\n",ans); }return 0; }