题解:P14521 【MX-S11-T2】加减乘除

· · 题解

Basic Observation

走到节点 $u$ 时,从根到 $u$ 路径上所有的点都会走过,记 $d_u=\sum_{x\in path_{1\to u}} a_x$,类似深度。 如果询问了一个 $x$,在 $u$ 节点时手上的数就是 $d_u+x$。 对于边 $(u,v,l,r)$,其中 $v$ 是 $u$ 的儿子,要走到 $v$ 有限制 $d_u+x\in [l,r]$,进而得出 $x\in [l-d_i,r-d_u]$,为什么想到这么做,因为后者是固定的值,前者是随询问变化的。 ## Solution I 首先是重工业做法,将本题加强后也能做。 如果将 $x$ 看作时刻,那么可以将原问题转化为,给定一颗树,树边 $(u,v,l_x,r_x)$ 表示这条边仅在时刻 $\in[l_x,r_x]$ 时出现,$q$ 次询问,查询某一时刻根节点所在的连通块大小,鉴定为动态图连通性问题,不难想到线段树分治。 维护连通块大小可以用并查集,用按秩合并并查集结合线段树分治的结构,可以实现连通性的撤销,时间复杂度 $O(n\log^2 n+q\log n)$,能过。 ``` #include<bits/stdc++.h> using namespace std; #define rep(i,l,r) for(int i=(l);i<=(r);++i) #define per(i,l,r) for(int i=(r);i>=(l);--i) #define pr pair<int,int> #define fi first #define se second #define pb push_back #define all(x) (x).begin(),(x).end() #define bg(x) (x).begin() #define ed(x) (x).end() #define sz(x) (int)(x).size() #define N 500011 #define V 2000010 #define ll long long // #define int long long int n,m,fa[N],u[N],v[N]; ll a[N],d[N]; struct edge{ int t; ll l,r; }; struct node{ int u,v; ll l,r; }; vector<edge>e[N]; vector<ll>ve; vector<node>ex; vector<pair<ll,int>>g; inline void dfs(int k){ d[k]=d[fa[k]]+a[k]; for(edge x:e[k]){ ll l=x.l-d[k],r=x.r-d[k]; if(l<=r){ ve.pb(l); ve.pb(r); ex.pb({k,x.t,l,r}); } dfs(x.t); } } int lim=0,ans[V]; namespace sol{ #define mid ((l+r)>>1) vector<int>e[V<<2],que[V]; inline void ins(int k,int l,int r,int L,int R,int id){ if(L<=l&&R>=r){ e[k].pb(id); return; } if(L<=mid){ ins(k*2,l,mid,L,R,id); } if(R>mid){ ins(k*2+1,mid+1,r,L,R,id); } } struct dsu{ int fa[N],siz[N],tp; pr st[N]; inline void init(){ rep(i,1,n){ fa[i]=i; siz[i]=1; } } inline int ask(int x){ while(x!=fa[x]){ x=fa[x]; } return x; } inline bool mg(int x,int y){ x=ask(x),y=ask(y); if(x==y){ return 0; } if(siz[x]>siz[y]){ swap(x,y); } fa[x]=y; siz[y]+=siz[x]; st[++tp]={x,y}; return 1; } inline void recall(int pos){ while(tp>pos){ auto [x,y]=st[tp]; siz[y]-=siz[x]; fa[x]=x; tp--; } } }d; inline void sol(int k,int l,int r){ int now=d.tp; for(int i:e[k]){ d.mg(ex[i].u,ex[i].v); } if(l==r){ for(int i:que[l]){ ans[i]=d.siz[d.ask(1)]; } d.recall(now); return; } sol(k*2,l,mid); sol(k*2+1,mid+1,r); d.recall(now); } #undef mid } signed main(){ // freopen("calc.in","r",stdin); // freopen("calc.out","w",stdout); ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin>>n>>m; rep(i,2,n){ ll l,r; cin>>fa[i]>>l>>r; e[fa[i]].pb({i,l,r}); } rep(i,1,n){ char o; cin>>o>>a[i]; if(o=='-'){ a[i]=-a[i]; } } dfs(1); rep(i,1,m){ ll x; cin>>x; ve.pb(x); g.pb({x,i}); } sort(all(ve)); ve.erase(unique(all(ve)),ed(ve)); lim=sz(ve); for(auto [x,i]:g){ x=lower_bound(all(ve),x)-bg(ve)+1; sol::que[x].pb(i); } g.clear(); rep(i,0,sz(ex)-1){ auto [u,v,l,r]=ex[i]; l=lower_bound(all(ve),l)-bg(ve)+1; r=lower_bound(all(ve),r)-bg(ve)+1; sol::ins(1,1,lim,l,r,i); } ve.clear(); sol::d.init(); sol::sol(1,1,lim); rep(i,1,m){ cout<<ans[i]<<'\n'; } return 0; } ``` ## Solution II 在树上维护动态图连通性还是太魔怔了,现在是正常做法。 注意到询问的是根节点,只需要关于某个节点与根的连通性。 注意到树上两个节点之间的路径只有一条,那么走到 $u$ 就需要,询问的 $x$ 满足从根结点出发到 $u$ 路径上所有节点关于 $x$ 大小的限制,限制是可以预处理的。 注意到限制是区间,限制之间的合并就是区间求交,将根到 $u$ 路径上的所有限制**求交**作为 $u$ 的**新限制**,那么 $x$ 满足这个限制意味着根一定能到 $u$,而不仅仅只是 $u$ 与 $fa_u$ 之间的边在当前 $x$ 下存在。 那么可以对每个节点 $i$ 求出 $[p_i,q_i]$ 表示 $x$ 的取值范围,使得 $i$ 能与根连通。 离散化后做用差分实现区间加即可,时间复杂度 $O((n+q)\log n)$,瓶颈在于排序。 ```cpp #include<bits/stdc++.h> using namespace std; #define rep(i,l,r) for(int i=(l);i<=(r);++i) #define per(i,l,r) for(int i=(r);i>=(l);--i) #define pr pair<int,int> #define fi first #define se second #define pb push_back #define all(x) (x).begin(),(x).end() #define bg(x) (x).begin() #define ed(x) (x).end() #define sz(x) (int)(x).size() #define N 500011 #define V 2000010 #define int __int128 int n,m,fa[N],u[N],v[N]; int a[N],d[N],ad[V]; pr b[N]; inline int rd(){ int x=0,f=1; char c=0; while(!isdigit(c)){ c=getchar_unlocked(); if(c=='-'){ f=-1; } } while(isdigit(c)){ x=10*x+c-'0'; c=getchar_unlocked(); } return x*f; } inline void out(int x){ if(x<0){ putchar_unlocked('-'); out(-x); return; } if(x>9){ out(x/10); } putchar_unlocked(x%10+'0'); } struct edge{ int t; int l,r; }; vector<edge>e[N]; vector<int>ve,que; vector<pr>g; bitset<N>f; inline void dfs(int k,int L,int R){ d[k]=d[fa[k]]+a[k]; for(edge x:e[k]){ int l=max(x.l-d[k],L),r=min(x.r-d[k],R); if(l<=r){ f[x.t]=1; b[x.t].fi=l; b[x.t].se=r; ve.pb(l);ve.pb(r); dfs(x.t,l,r); } } } int lim=0,ans[V]; signed main(){ // freopen("calc.in","r",stdin); // freopen("calc.out","w",stdout); // ios::sync_with_stdio(0); // cin.tie(0);cout.tie(0); n=rd(),m=rd(); rep(i,2,n){ int l,r; fa[i]=rd(); l=rd();r=rd(); e[fa[i]].pb({i,l,r}); } rep(i,1,n){ char o; cin>>o; a[i]=rd(); if(o=='-'){ a[i]=-a[i]; } } dfs(1,-1e30,1e30); rep(i,1,m){ int x=rd(); ve.pb(x); que.pb(x); } sort(all(ve)); ve.erase(unique(all(ve)),ed(ve)); lim=sz(ve); rep(i,1,n){ if(f[i]){ auto [l,r]=b[i]; l=lower_bound(all(ve),l)-bg(ve)+1; r=lower_bound(all(ve),r)-bg(ve)+1; ad[l]++; ad[r+1]--; } } rep(i,1,lim){ ad[i]+=ad[i-1]; } for(int x:que){ x=lower_bound(all(ve),x)-bg(ve)+1; out(ad[x]+1); putchar_unlocked('\n'); } return 0; } ```