P10789 [NOI2024] 登山
思路:
我们可以对于每个
考虑动态规划算法,令
那么相当于先从
则状态转移方程为:
其中
朴素实现是
Code:
namespace Sub1{ ll L,R,x,a,b; ll s[N],dp[N],dep[N]; void dfs1(ll u){ for(auto v:E[u]){ if(v==fa[u]) continue; dep[v]=dep[u]+1; dfs1(v); } } ll get(ll l,ll r){ if(!l) return s[r]; return (s[r]-s[l-1]+mod)%mod; } void dfs3(ll u,ll Min,ll from){ L=l[u],R=min(r[u],Min); if(L<=R) dp[from]=(dp[from]+get(L,R))%mod; for(auto v:E[u]){ if(v==fa[u]) continue; dfs3(v,min(Min,h[v]),from); } } void dfs2(ll u){ if(dep[u]) s[dep[u]]=(s[dep[u]-1]+dp[u])%mod; else s[dep[u]]=dp[u]; for(auto v:E[u]){ if(v==fa[u]) continue; dfs3(v,h[v],v); dfs2(v); } } void solve(){ Read(); dfs1(1); dp[1]=1; For(i,2,n){ dp[i]=0; x=i; h[i]=dep[i]-h[i]-1; while(r[i]){ x=fa[x]; l[i]--,r[i]--; if(!l[i]) b=x; if(!r[i]) a=x; } l[i]=dep[a],r[i]=dep[b]; } dfs2(1); For(i,2,n){ write(dp[i]); putchar(' '); } putchar('\n'); } void work(){ while(T--) solve(); } };
设
现在考虑
则我们考虑枚举
那么对于
按照
需要维护路径加,单点查,可以直接树剖与树状数组或线段树,时间复杂度为
Code:
class Tree{ public: ll cnt=0; ll p[N],t[N],z[N],d[N],fa[N]; struct Node{ ll l,r; ll data; ll tag; }X[N<<2]; void dfs1(ll u,ll f){ p[u]=1; for(auto v:E[u]){ if(v==f) continue; d[v]=d[u]+1; fa[v]=u; dfs1(v,u); p[u]+=p[v]; if(p[v]>p[z[u]]) z[u]=v; } } void dfs2(ll u,ll k){ dfn[u]=++cnt; t[u]=k; if(!z[u]) return ; dfs2(z[u],k); for(auto v:E[u]){ if(v==fa[u]||v==z[u]) continue; dfs2(v,v); } } void build(ll k,ll l,ll r){ X[k].l=l,X[k].r=r; X[k].data=X[k].tag=0; if(l==r) return ; ll mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } void add(ll k,ll v){ X[k].data=Add(X[k].data,v); X[k].tag=Add(X[k].tag,v); } void push_down(ll k){ if(X[k].tag){ add(k<<1,X[k].tag); add(k<<1|1,X[k].tag); X[k].tag=0; } } void update(ll k,ll l,ll r,ll v){ if(X[k].l==l&&r==X[k].r){ add(k,v); return ; } push_down(k); ll mid=(X[k].l+X[k].r)>>1; if(r<=mid) update(k<<1,l,r,v); else if(l>mid) update(k<<1|1,l,r,v); else{ update(k<<1,l,mid,v); update(k<<1|1,mid+1,r,v); } } ll query(ll k,ll i){ if(X[k].l==i&&i==X[k].r) return X[k].data; push_down(k); ll mid=(X[k].l+X[k].r)>>1; if(i<=mid) return query(k<<1,i); else return query(k<<1|1,i); } void update(ll u,ll v,ll h){ while(t[u]!=t[v]){ if(d[t[u]]<d[t[v]]) swap(u,v); update(1,dfn[t[u]],dfn[u],h); u=fa[t[u]]; } if(d[u]>d[v]) swap(u,v); update(1,dfn[u],dfn[v],h); } void init(){ For(i,1,n) z[i]=0; cnt=0; dfs1(1,1); dfs2(1,1); build(1,1,n); } }Tr; namespace Sub2{ ll x,cnt; ll g[N],dep[N],Fa[N][M],F[N][M]; vector<pi> G[N]; void dfs1(ll u){ For(j,1,M-1) Fa[u][j]=Fa[Fa[u][j-1]][j-1]; for(auto v:E[u]){ if(v==fa[u]) continue; dep[v]=dep[u]+1; Fa[v][0]=u; dfs1(v); } } ll get_fa(ll u,ll k){ if(!k) return u; _For(j,0,M-1){ if(k>=(1ll<<j)){ k-=(1ll<<j); u=Fa[u][j]; } } return u; } void dfs2(ll u){ For(j,1,M-1) F[u][j]=min(F[u][j-1],F[Fa[u][j-1]][j-1]); for(auto v:E[u]){ if(v==fa[u]) continue; F[v][0]=h[v]; dfs2(v); } } void solve(){ cnt=0; Read(); Tr.init(); For(j,0,M-1) Fa[1][j]=1; dfs1(1); For(i,0,n-1) G[i].clear(); For(i,2,n){ x=get_fa(i,l[i]); l[i]=r[i]=dep[x]; h[i]=dep[i]-h[i]-1; G[l[i]].push_back({i,x}); } For(j,0,M-1) F[1][j]=INF; dfs2(1); For(i,2,n){ x=i; _For(j,0,M-1) if(r[i]<=F[x][j]) x=Fa[x][j]; g[i]=dep[x]+1; if(g[i]<=dep[i]) g[i]=get_fa(i,dep[i]-g[i]); else g[i]=-1; } Tr.update(1,1,1); For(i,0,n-1){ if(G[i].empty()) continue; for(auto t:G[i]){ ll v=t.fi; if(g[v]==-1) continue; Tr.update(v,g[v],Tr.query(1,dfn[t.se])); } } For(i,2,n){ write(Tr.query(1,dfn[i])); putchar(' '); } putchar('\n'); } void work(){ while(T--) solve(); } };
现在考虑根据
还是一样,枚举
考虑分开计算,发现对于
现在是如何处理
然后考虑处理
只要求出了这样的
注意到这样操作会有先后顺序的问题,考虑再求出
该部分暴力 Code:
namespace Sub3{ ll Min,sum,x,a,b,c,t; ll dep[N],dp[N],g1[N],g2[N],g3[N]; ll Fa[N][M]; vector<pi> E1[N],E2[N],E3[N]; void dfs(ll u){ For(j,1,M-1) Fa[u][j]=Fa[Fa[u][j-1]][j-1]; for(auto v:E[u]){ if(v==fa[u]) continue; Fa[v][0]=u; dep[v]=dep[u]+1; dfs(v); } } ll get_fa(ll u,ll k){ if(!k) return u; _For(j,0,M-1){ if(k>=(1ll<<j)){ k-=(1ll<<j); u=Fa[u][j]; } } return u; } ll get_sum(ll u){ ll ans=0; while(u){ ans=Add(ans,dp[u]); u=fa[u]; } return ans; } void dfs2(ll u,ll k){ if(h[u]<=h[k]&&u!=k) return ; if(l[u]<=h[k]&&h[k]<=r[u]) sum++; for(auto v:E[u]){ if(v==fa[u]) continue; dfs2(v,k); } } void solve(){ Read(); dfs(1); dp[1]=1; For(i,0,n-1){ E1[i].clear(); E2[i].clear(); E3[i].clear(); } For(i,2,n){ dp[i]=0; x=i; t=h[i]+1; h[i]=dep[i]-h[i]-1; while(l[i]>=1||r[i]>=1||t>=1){ x=fa[x]; l[i]--,r[i]--,t--; if(!l[i]) b=x; if(!r[i]) a=x; if(!t) c=x; } l[i]=dep[a],r[i]=dep[b]; if(l[i]) E1[l[i]-1].push_back({i,fa[a]}); E2[r[i]].push_back({i,b}); E3[h[i]].push_back({i,c}); } //cerr<<'\n'; For(i,2,n){ if(l[i]){ Min=INF; x=i,a=-1; while(fa[x]!=1){ Min=min(Min,h[x]); if(Min<l[i]) break; a=x; x=fa[x]; } g1[i]=a; } else g1[i]=-1; Min=INF; x=i,a=-1; while(fa[x]!=1){ Min=min(Min,h[x]); if(Min<=r[i]) break; a=x; x=fa[x]; } g2[i]=a; x=i; while(fa[x]!=1){ if(h[fa[x]]<h[i]) break; x=fa[x]; } g3[i]=x; //cerr<<i<<':'<<g1[i]<<','<<g2[i]<<','<<g3[i]<<'\n'; } //cerr<<'\n'; For(i,0,n-1){ for(auto t:E1[i]){ x=t.fi,a=get_sum(t.se),b=g1[x]; if(b==-1) continue; //cerr<<"("<<x<<"->"<<b<<")-"<<a<<'\n'; while(x!=fa[b]){ dp[x]=(dp[x]-a+mod)%mod; x=fa[x]; } } for(auto t:E2[i]){ x=t.fi,a=get_sum(t.se),b=g2[x]; if(b==-1) continue; //cerr<<"("<<x<<"->"<<b<<")+"<<a<<'\n'; while(x!=fa[b]){ dp[x]=Add(dp[x],a); x=fa[x]; } } for(auto t:E3[i]){ x=t.fi,a=get_sum(t.se),b=g3[x]; if(b==-1) continue; sum=0; dfs2(x,x); //cerr<<"("<<x<<"->"<<b<<")+"<<sum<<'*'<<a<<'\n'; while(x!=fa[b]){ dp[x]=(dp[x]+sum*a%mod)%mod; x=fa[x]; } } } For(i,2,n){ write(dp[i]); putchar(' '); } putchar('\n'); } void work(){ while(T--) solve(); } };
这样的话操作都是一些基本操作,链加,链查,倍增优化等,唯一需要注意的是查询
考虑对于
根据这个
那么可以每次将
考虑对于新树求出 dfn 序,则
也可以使用 dsu on tree 做,但是本人太菜不会。
总时间复杂度为
注意主席树的空间要开
完整代码;
#include<bits/stdc++.h> #define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y) #define lowbit(x) x&(-x) #define pi pair<ll,ll> #define pii pair<ll,pair<ll,ll>> #define iip pair<pair<ll,ll>,ll> #define ppii pair<pair<ll,ll>,pair<ll,ll>> #define fi first #define se second #define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x #define Full(a) memset(a,0,sizeof(a)) #define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout); #define For(i,l,r) for(int i=l;i<=r;i++) #define _For(i,l,r) for(int i=r;i>=l;i--) using namespace std; typedef double db; typedef unsigned long long ull; typedef long long ll; const ll N=1e5+10,M=20,mod=998244353,INF=1e9; inline ll read(){ ll x=0,f=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } inline void write(ll x){ if(x<0){ putchar('-'); x=-x; } if(x>9) write(x/10); putchar(x%10+'0'); } int T,n; int root[N],fa[N],l[N],r[N],h[N],id[N],_id[N],dfn[N]; vector<int> E[N],G[N]; inline void add(int u,int v){ E[u].push_back(v); E[v].push_back(u); } inline void ADD(int u,int v){ G[u].push_back(v); G[v].push_back(u); } inline void Read(){ n=read(); For(i,1,n){ E[i].clear(); G[i].clear(); } For(i,2,n){ fa[i]=read(),l[i]=read(),r[i]=read(),h[i]=read(); add(fa[i],i); } } class Tree{ public: int cnt=0; int p[N],t[N],z[N],d[N],fa[N]; struct Node{ int l,r,len; ll data; ll tag; }X[N<<2]; inline void dfs1(int u,int f){ p[u]=1; for(auto v:E[u]){ if(v==f) continue; d[v]=d[u]+1; fa[v]=u; dfs1(v,u); p[u]+=p[v]; if(p[v]>p[z[u]]) z[u]=v; } } inline void dfs2(int u,int k){ dfn[u]=++cnt; t[u]=k; if(!z[u]) return ; dfs2(z[u],k); for(auto v:E[u]){ if(v==fa[u]||v==z[u]) continue; dfs2(v,v); } } inline void build(int k,int l,int r){ X[k].len=r-l+1; X[k].l=l,X[k].r=r; X[k].data=X[k].tag=0; if(l==r) return ; int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } inline void pushup(int k){ X[k].data=Add(X[k<<1].data,X[k<<1|1].data); } inline void add(int k,int v){ X[k].data=(X[k].data+1ll*X[k].len*v%mod)%mod; X[k].tag=Add(X[k].tag,v); } inline void push_down(int k){ if(X[k].tag){ add(k<<1,X[k].tag); add(k<<1|1,X[k].tag); X[k].tag=0; } } inline void update(int k,int l,int r,int v){ if(X[k].l==l&&r==X[k].r){ add(k,v); return ; } push_down(k); int mid=(X[k].l+X[k].r)>>1; if(r<=mid) update(k<<1,l,r,v); else if(l>mid) update(k<<1|1,l,r,v); else{ update(k<<1,l,mid,v); update(k<<1|1,mid+1,r,v); } pushup(k); } inline int query(int k,int l,int r){ if(X[k].l==l&&r==X[k].r) return X[k].data; push_down(k); ll mid=(X[k].l+X[k].r)>>1; if(r<=mid) return query(k<<1,l,r); else if(l>mid) return query(k<<1|1,l,r); else return (query(k<<1,l,mid)+query(k<<1|1,mid+1,r))%mod; } inline int query(int k,int i){ if(X[k].l==i&&i==X[k].r) return X[k].data; push_down(k); ll mid=(X[k].l+X[k].r)>>1; if(i<=mid) return query(k<<1,i); else return query(k<<1|1,i); } inline void update(int u,int v,int h){ while(t[u]!=t[v]){ if(d[t[u]]<d[t[v]]) swap(u,v); update(1,dfn[t[u]],dfn[u],h); u=fa[t[u]]; } if(d[u]>d[v]) swap(u,v); update(1,dfn[u],dfn[v],h); } inline int ask(int u,int v){ int ans=0; while(t[u]!=t[v]){ if(d[t[u]]<d[t[v]]) swap(u,v); ans=(ans+query(1,dfn[t[u]],dfn[u]))%mod; u=fa[t[u]]; } if(d[u]>d[v]) swap(u,v); ans=(ans+query(1,dfn[u],dfn[v]))%mod; return ans; } inline void init(){ For(i,1,n) z[i]=0; cnt=0; dfs1(1,1); dfs2(1,1); build(1,1,n); } }Tr; namespace Seg{ int siz[N]; int cnt,s; struct Node{ int L,R; int tag; }X[N*50]; inline void dfs(int u,int fa){ id[u]=++s; _id[s]=u; siz[u]=1; for(auto v:G[u]){ if(v==fa) continue; dfs(v,u); siz[u]+=siz[v]; } } inline void tidy(){ For(i,1,cnt) X[i]={0,0,0}; s=cnt=0; dfs(1,1); } inline void build(int &k,int l,int r){ if(!k) k=++cnt; X[k].tag=0; if(l==r) return ; int mid=(l+r)>>1; build(X[k].L,l,mid); build(X[k].R,mid+1,r); } inline void update(int &k,int l,int r,int L,int R,int v){ X[++cnt]=X[k]; k=cnt; if(l==L&&r==R){ X[k].tag=Add(X[k].tag,v); return ; } ll mid=(l+r)>>1; if(R<=mid) update(X[k].L,l,mid,L,R,v); else if(L>mid) update(X[k].R,mid+1,r,L,R,v); else{ update(X[k].L,l,mid,L,mid,v); update(X[k].R,mid+1,r,mid+1,R,v); } } inline void Ask(int k,int l,int r,int i,int &ans){ ans=Add(ans,X[k].tag); if(l==i&&i==r) return ; int mid=(l+r)>>1; if(i<=mid) Ask(X[k].L,l,mid,i,ans); else Ask(X[k].R,mid+1,r,i,ans); } inline int Ask(int u){ int X=0,Y=0; Ask(root[id[u]+siz[u]-1],0,n,h[u],X); Ask(root[id[u]-1],0,n,h[u],Y); return (X-Y+mod)%mod; } }; namespace Std{ int Min,sum,x,a,b,c,t; int dep[N],g1[N],g2[N],g3[N]; int Fa[N][M],F[N][M]; vector<pi> E1[N],E2[N],E3[N]; inline void dfs(int u){ For(j,1,M-1) Fa[u][j]=Fa[Fa[u][j-1]][j-1]; for(auto v:E[u]){ if(v==fa[u]) continue; Fa[v][0]=u; dep[v]=dep[u]+1; dfs(v); } } inline int get_fa(int u,int k){ if(!k) return u; _For(j,0,M-1){ if(k>=(1ll<<j)){ k-=(1ll<<j); u=Fa[u][j]; } } return u; } inline void dfs1(int u){ For(i,1,M-1) F[u][i]=min(F[u][i-1],F[Fa[u][i-1]][i-1]); for(auto v:E[u]){ if(v==fa[u]) continue; F[v][0]=h[v]; dfs1(v); } } inline void solve(){ Read(); For(j,0,M-1) Fa[1][j]=1; dfs(1); For(i,1,n) root[i]=0; For(i,0,n-1){ E1[i].clear(); E2[i].clear(); E3[i].clear(); } For(i,2,n){ a=get_fa(i,r[i]),b=get_fa(i,l[i]),c=get_fa(i,h[i]+1); h[i]=dep[i]-h[i]-1; l[i]=dep[a],r[i]=dep[b]; if(l[i]) E1[l[i]-1].push_back({i,fa[a]}); E2[r[i]].push_back({i,b}); E3[h[i]].push_back({i,c}); } For(j,0,M-1) F[1][j]=INF; dfs1(1); For(i,2,n){ if(l[i]){ x=i; _For(j,0,M-1) if(F[x][j]>=l[i]) x=Fa[x][j]; if(x==i) g1[i]=-1; else g1[i]=get_fa(i,dep[i]-dep[x]-1); } else g1[i]=-1; x=i; _For(j,0,M-1) if(F[x][j]>r[i]) x=Fa[x][j]; if(x==i) g2[i]=-1; else g2[i]=get_fa(i,dep[i]-dep[x]-1); Min=INF,x=i; _For(j,0,M-1) if(F[x][j]>=h[i]) x=Fa[x][j]; ADD(i,x); if(x==i) g3[i]=-1; else g3[i]=get_fa(i,dep[i]-dep[x]-1); } Tr.init(); Tr.update(1,1,1); Seg::tidy(); Seg::build(root[1],0,n); For(i,2,n){ root[i]=root[i-1]; Seg::update(root[i],0,n,l[_id[i]],r[_id[i]],1); a=0; Seg::Ask(root[i],0,n,0,a); } For(i,0,n-1){ for(auto t:E1[i]){ x=t.fi,b=g1[x]; if(b==-1) continue; a=Tr.ask(1,t.se); Tr.update(x,b,mod-a); } for(auto t:E2[i]){ x=t.fi,b=g2[x]; if(b==-1) continue; a=Tr.ask(1,t.se); Tr.update(x,b,a); } for(auto t:E3[i]){ x=t.fi,b=g3[x]; if(b==-1) continue; a=Tr.ask(1,t.se); sum=Seg::Ask(x); Tr.update(x,b,1ll*sum*a%mod); } } For(i,2,n){ write(Tr.query(1,dfn[i])); putchar(' '); } putchar('\n'); } inline void work(){ while(T--) solve(); } }; int main(){ //open("A.in","A.out"); read(),T=read(); Std::work(); return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 不到万不得已,千万不要去外包
· C# WebAPI 插件热插拔(持续更新中)
· 会议真的有必要吗?我们产品开发9年了,但从来没开过会
· 如何打造一个高并发系统?
· 《SpringBoot》EasyExcel实现百万数据的导入导出