P5298 [PKUWC2018] Minimax - 线段树合并
题意
给定一棵
设
题解
因为
设
右儿子同理。
考虑线段树合并的过程是怎么保证复杂度的:在
所以,对于
- 假如
为空:此时相当于 这个节点对应的区间内每一个位置都乘上一个数,打区间乘的标记即可。 - 假如
为空:同理。 - 都不为空:递归下去,然后上传新的值。
但转移方程内层的
定义
待补:有懒标记的线段树合并方法。
时间复杂度
代码
看起来我的
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
#define Debug(...) fprintf(stderr,__VA_ARGS__)
typedef long long ll;
const int N=3e5+5,LogN=20,Mod=998244353;
long long Pow(long long _base,long long _pow,const long long& _mod){
long long _res=1;
while(_pow){
if(_pow&1) _res=_res*_base%_mod;
_pow>>=1,_base=_base*_base%_mod;
}
return _res%_mod;
}
struct SegmentTree{
struct Node{
int l,r,ls,rs;
ll s,Mul=1;
void PushMul(ll k){s=s*k%Mod,Mul=Mul*k%Mod;}
}t[N*LogN];
int tot=0;
int New(int l,int r){
t[++tot].l=l,t[tot].r=r;
return tot;
}
void Pushup(int p){t[p].s=(t[t[p].ls].s+t[t[p].rs].s)%Mod;}
void Pushdown(int p){
if(!p) return;
if(t[p].Mul!=1) t[t[p].ls].PushMul(t[p].Mul),t[t[p].rs].PushMul(t[p].Mul);
t[p].Mul=1;
}
void Modify(int p,int pos,ll k){
if(t[p].l==t[p].r){
t[p].s=k;return;
}
Pushdown(p);
int mid=(t[p].l+t[p].r)/2;
if(pos<=mid) Modify(t[p].ls?t[p].ls:(t[p].ls=New(t[p].l,mid)),pos,k);
else Modify(t[p].rs?t[p].rs:(t[p].rs=New(mid+1,t[p].r)),pos,k);
Pushup(p);
}
int Merge(int p,int q,ll ps,ll qs,ll prob){
if(!p&&!q) return 0;
if(!p||!q){
if(!p) swap(p,q),swap(ps,qs);
t[p].PushMul((qs*prob+(Mod+1-qs)*(Mod+1-prob))%Mod);
return p;
}
Pushdown(p),Pushdown(q);
ll pls=t[t[p].ls].s,qls=t[t[q].ls].s;
t[p].ls=Merge(t[p].ls,t[q].ls,ps,qs,prob);
t[p].rs=Merge(t[p].rs,t[q].rs,(ps+pls)%Mod,(qs+qls)%Mod,prob);
Pushup(p);
return p;
}
void Get(int p,vector<int> &res){
if(!p) return;
if(t[p].l==t[p].r){
res.push_back(t[p].s);return;
}
Pushdown(p);
Get(t[p].ls,res),Get(t[p].rs,res);
}
}seg;
int n,root[N],ch[N][2],discv[N];ll p[N];
vector<ll> disc;
void Dfs(int u){
if(!ch[u][0]){
root[u]=seg.New(1,disc.size()),seg.Modify(root[u],discv[u],1);
}else if(!ch[u][1]){
Dfs(ch[u][0]);
root[u]=root[ch[u][0]];
}else{
Dfs(ch[u][0]),Dfs(ch[u][1]);
root[u]=seg.Merge(root[ch[u][0]],root[ch[u][1]],0,0,p[u]);
}
}
int main(){
#ifndef zyz
ios::sync_with_stdio(false),cin.tie(nullptr);
#endif
cin>>n;
For(i,1,n){
int fa;cin>>fa;
if(fa) ch[fa][ch[fa][0]!=0]=i;
}
ll inv1w=Pow(10000,Mod-2,Mod);
For(i,1,n){
ll x;cin>>x;
if(!ch[i][0]) p[i]=x,disc.push_back(x);
else p[i]=1LL*x*inv1w%Mod;
}
sort(disc.begin(),disc.end());
For(i,1,n) if(!ch[i][0]) discv[i]=lower_bound(disc.begin(),disc.end(),p[i])-disc.begin()+1;
Dfs(1);
vector<int> ans;
ans.push_back(0);
seg.Get(root[1],ans);
ll Ans=0;
for(int i=1;i<int(ans.size());++i){
Ans=(Ans+1LL*i*disc[i-1]%Mod*ans[i]%Mod*ans[i]%Mod)%Mod;
}
cout<<Ans;
return 0;
}
作者:alan-zhao-2007
出处:https://www.cnblogs.com/alan-zhao-2007/p/p5298-sol.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
Written by Alan_Zhao
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通