BZOJ3644 : 陶陶的旅行计划
假设是序列问题,且$S<T$,可以贪心求解,通过维护下述信息进行区间合并。
对于区间$[l,r]$,维护的信息有:
$v$:跳到了$\geq r$的位置后,可以花费$1$往右最多扩展多少。
$f[i]$:从$\leq l+i$开始跳到$\geq r$的位置的最少步数。
$g[i]$:从$\leq l+i$开始跳到$\geq r$的位置时,在$f$最小的基础上,可以花费$0$往右最多扩展多少。
然后树链剖分+线段树维护信息即可。
时间复杂度$O(am\log^2n)$。
#include<cstdio> #include<algorithm> using namespace std; const int N=100005,M=262150,K=20; int n,m,i,x,y,a[N],g[N],v[N<<1],nxt[N<<1],ed,f[N],d[N],size[N],son[N],top[N],loc[N],q[N],dfn;char op; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} inline void merge(int&f,int&g,int x,int y){if(x<f||x==f&&y>g)f=x,g=y;} struct P{ int f[K],g[K],v,len; P(){} P operator+(const P&b){ P c; for(int i=0;i<K;i++){ if(i<len){ if(g[i]>=b.len){ c.f[i]=f[i]; c.g[i]=g[i]-b.len; }else if(g[i]){ c.f[i]=f[i]+b.f[g[i]-1]; c.g[i]=b.g[g[i]-1]; }else c.f[i]=N; if(v>=b.len)merge(c.f[i],c.g[i],f[i]+1,v-b.len); else merge(c.f[i],c.g[i],f[i]+b.f[v-1]+1,b.g[v-1]); }else{ if(i-len>=b.len)break; c.f[i]=b.f[i-len]; c.g[i]=b.g[i-len]; } } c.v=max(v-b.len,b.v); c.len=len+b.len; for(int i=1;i<K;i++)if(c.f[i]>c.f[i-1]||c.f[i]==c.f[i-1]&&c.g[i]<c.g[i-1])c.f[i]=c.f[i-1],c.g[i]=c.g[i-1]; for(int i=c.len;i<K;i++)c.f[i]=N,c.g[i]=0; return c; } void set(int x){ v=x,len=1; f[0]=g[0]=0; for(int i=1;i<K;i++)f[i]=N,g[i]=0; } }vl[M],vr[M],tmp;bool flag; inline void up(int x){ vl[x]=vl[x<<1]+vl[x<<1|1]; vr[x]=vr[x<<1|1]+vr[x<<1]; } void build(int x,int a,int b){ if(a==b){ vl[x].set(q[a]); vr[x].set(q[a]); return; } int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid+1,b),up(x); } void change(int x,int a,int b,int c,int d){ if(a==b){ vl[x].set(d); vr[x].set(d); return; } int mid=(a+b)>>1; if(c<=mid)change(x<<1,a,mid,c,d); else change(x<<1|1,mid+1,b,c,d); up(x); } void askl(int x,int a,int b,int c,int d){ if(c<=a&&b<=d){ if(flag)tmp=tmp+vl[x];else tmp=vl[x],flag=1; return; } int mid=(a+b)>>1; if(c<=mid)askl(x<<1,a,mid,c,d); if(d>mid)askl(x<<1|1,mid+1,b,c,d); } void askr(int x,int a,int b,int c,int d){ if(c<=a&&b<=d){ if(flag)tmp=tmp+vr[x];else tmp=vr[x],flag=1; return; } int mid=(a+b)>>1; if(d>mid)askr(x<<1|1,mid+1,b,c,d); if(c<=mid)askr(x<<1,a,mid,c,d); } inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} void dfs(int x){ size[x]=1;d[x]=d[f[x]]+1; for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){ f[v[i]]=x,dfs(v[i]); size[x]+=size[v[i]]; if(size[v[i]]>size[son[x]])son[x]=v[i]; } } void dfs2(int x,int y){ q[loc[x]=++dfn]=a[x];top[x]=y; if(son[x])dfs2(son[x],y); for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]&&v[i]!=son[x])dfs2(v[i],v[i]); } inline int lca(int x,int y){ for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]])swap(x,y); return d[x]<d[y]?x:y; } int cnt,pool[1000][2]; inline int query(int x,int y){ int z=lca(x,y); flag=cnt=0; while(top[x]!=top[z]){ askr(1,1,n,loc[top[x]],loc[x]); x=f[top[x]]; } askr(1,1,n,loc[z],loc[x]); while(top[y]!=top[z]){ pool[++cnt][0]=loc[top[y]]; pool[cnt][1]=loc[y]; y=f[top[y]]; } if(y!=z){ pool[++cnt][0]=loc[z]+1; pool[cnt][1]=loc[y]; } for(int i=cnt;i;i--)askl(1,1,n,pool[i][0],pool[i][1]); return tmp.f[0]; } int main(){ read(n); for(i=1;i<=n;i++)read(a[i]); for(i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x); dfs(1); dfs2(1,1); build(1,1,n); read(m); while(m--){ while((op=getchar())!='Q'&&op!='C'); read(x),read(y); if(op=='C')change(1,1,n,loc[x],y); else printf("%d\n",query(x,y)); } return 0; }