BZOJ3672 [Noi2014]购票
题解:
首先dp方程很简单 dp[i]=min(dp[j]+p[i]*dist(i,j)+q[i])
显然是可以斜率优化的
(dp[j]-dp[k])/(dis[j]-dis[k])<p[i]
那么我们的目的就是要维护这个凸包
由于p[i]不具有单调性,所以最优解是要三分的
然后我们先考虑这个问题在链上
现在的问题在于,还有li这个限制
于是我们可以选择建立线段树,对线段树每个区间维护其内的凸包
这样我们可以在nlog^2 n的时间内完成链上
考虑回到树上
我们可以直接树剖 这样是nlog^3 n的
由于树剖常数很小,所以也可以过啊
线段树和树剖写的都很顺利。。然后就wa了
对拍了发现又犯了个智障错误。。 先改了ans再查。。
不知道为什么还能过那么多
为了比较简单 我都用了ll。。懒得改int
#include <bits/stdc++.h> using namespace std; #define ll long long #define IL inline #define rint register ll #define rep(i,h,t) for (rint i=h;i<=t;i++) #define dep(i,t,h) for (rint i=t;i>=h;i--) IL ll max(ll x,ll y) { if (x>y) return(x); else return(y); } IL ll min(ll x,ll y) { if (x<y) return(x); else return(y); } IL void swap(ll &x,ll &y) { ll tmp=x; x=y; y=tmp; } char ss[1<<27],*A=ss,*B=ss; IL char gc() { return A==B&&(B=(A=ss)+fread(ss,1,1<<27,stdin),A==B)?EOF:*A++; } template<class T> void read(T &x) { rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=c^48; while (c=gc(),47<c&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; } #define mid ((h+t)/2) const ll N=5.3e5; const ll INF=1e18; ll now[N],p[N],q[N],head[N],size[N],son[N],fa[N],top[N],id[N],real2[N],dep[N],dis[N],dp[N],l[N],lx,num; ll bz1[20][N],bz2[20][N],n,m; struct re{ ll a,b,c,d; }a[N]; vector<ll> ve[N*4]; vector<re> ve1[N]; IL void arr(ll x,ll y,ll z) { a[++lx].a=head[x]; a[lx].b=y; a[lx].c=z; head[x]=lx; } void dfs1(ll x,ll y) { ll u=head[x]; fa[x]=y; son[x]=-1; size[x]=1; dep[x]=dep[y]+1; while(u) { ll v=a[u].b; dis[v]=dis[x]+a[u].c; dfs1(v,x); bz1[0][v]=x; bz2[0][v]=a[u].c; size[x]+=size[v]; if (son[x]==-1||size[v]>size[son[x]]) son[x]=v; u=a[u].a; } } void dfs2(ll x,ll y) { top[x]=y; id[x]=++num; real2[num]=x; if (son[x]==-1) return; dfs2(son[x],y); ll u=head[x]; while (u) { ll v=a[u].b; if (v!=son[x]) dfs2(v,v); u=a[u].a; } } IL double check1(ll x,ll y) { return(1.00000000*(dp[x]-dp[y])/(dis[x]-dis[y])); } ll query(ll x,ll h,ll t,ll h1,ll t1,ll k) { if (h1<=h&&t<=t1) { if (!now[x]) return(INF); ll l=0,r=now[x]-1; while (l<r) { ll midd=(l+r)/2; if (check1(ve[x][midd+1],ve[x][midd])<=p[k]) l=midd+1; else r=midd; } ll xx=ve[x][l]; return(dp[xx]+p[k]*(dis[k]-dis[xx])+q[k]); } ll ans=INF; if (h1<=mid) ans=query(x*2,h,mid,h1,t1,k); if (mid<t1) ans=min(ans,query(x*2+1,mid+1,t,h1,t1,k)); return(ans); } IL bool check2(ll x,ll y,ll z) { if (1.00000000*(dp[z]-dp[x])/(dis[z]-dis[x])>=1.0000000*(dp[x]-dp[y])/(dis[x]-dis[y])) return(1); else return(0); } void change(ll x,ll h,ll t,ll pos,ll k) { ll l=0,r=now[x]-1; while (l<r) { ll midd=(l+r)/2; if (check2(ve[x][midd+1],ve[x][midd],k)) l=midd+1; else r=midd; } //ve1[k].push_back((re){x,now[k],l+1,l+1<=now[k]?ve[k][l+1]:-1}); if (!now[x]) now[x]=1; else now[x]=l+2; //if (now[x]>=ve[x].size()) ve[x].push_back(k); else ve[x][l+1]=k; if (now[x]>ve[x].size()) ve[x].push_back(k); else ve[x][now[x]-1]=k; if (h==t) return; if (pos<=mid) change(x*2,h,mid,pos,k); else change(x*2+1,mid+1,t,pos,k); } void dfs3(ll x) { ll f1=top[x],xy=x; ll ans=INF; while (x&&dep[l[xy]]<=dep[x]) { if (dep[l[xy]]<=dep[f1]) ans=min(ans,query(1,1,n,id[f1],id[x],xy)); else ans=min(ans,query(1,1,n,id[l[xy]],id[x],xy)); x=fa[f1]; f1=top[x]; } x=xy; if (x!=1) dp[x]=ans; else dp[x]=0; change(1,1,n,id[x],x); ll u=head[x]; while (u) { ll v=a[u].b; dfs3(v); u=a[u].a; } /* rep(i,0,ve1[x].size()) { re xx=ve1[x][i]; now[xx.a]=xx.b; if (xx.d!=-1) { ve[xx.a][xx.c]=xx.d; } } */ } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); read(n); read(m); rep(i,2,n) { ll x,y; read(x); read(y); arr(x,i,y); read(p[i]); read(q[i]); read(l[i]); } dfs1(1,0); dfs2(1,1); rep(i,1,19) rep(j,1,n) { bz1[i][j]=bz1[i-1][bz1[i-1][j]]; bz2[i][j]=bz2[i-1][j]+bz2[i-1][bz1[i-1][j]]; } rep(i,1,n) { ll ans=i; dep(j,19,0) if (l[i]>=bz2[j][ans]) l[i]-=bz2[j][ans],ans=bz1[j][ans]; l[i]=max(ans,1); } dfs3(1); rep(i,2,n) printf("%lld\n",dp[i]); return 0; }
考虑一下理论复杂度更小的办法
现在我们需要的是一个支持撤销的操作
由于每次修改,我们只会修改栈顶位置和当前栈顶位置的元素
这个显然我们是可以通过vector记录来完成的
这样是nlog^2n的
然而 事实是 这个跑的比树剖
我也不是很理解为什么啊。。
常数应该就是2倍啊
难道开了o2vector还是那么慢啊??
树剖的log难道<2??
#include <bits/stdc++.h> using namespace std; #define ll long long #define IL inline #define rint register ll #define rep(i,h,t) for (rint i=h;i<=t;i++) #define dep(i,t,h) for (rint i=t;i>=h;i--) IL ll max(ll x,ll y) { if (x>y) return(x); else return(y); } IL ll min(ll x,ll y) { if (x<y) return(x); else return(y); } IL void swap(ll &x,ll &y) { ll tmp=x; x=y; y=tmp; } char ss[1<<27],*A=ss,*B=ss; IL char gc() { return A==B&&(B=(A=ss)+fread(ss,1,1<<27,stdin),A==B)?EOF:*A++; } template<class T> void read(T &x) { rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=c^48; while (c=gc(),47<c&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; } #define mid ((h+t)/2) //const ll N=6.3e5; const int N2=2.3e5; const ll INF=1e18; ll now[N2*4],p[N2],q[N2],head[N2]; ll dep[N2],dis[N2],dp[N2],l[N2],lx,num; int bz1[20][N2]; ll bz2[20][N2],n,m; struct re{ ll a,b,c,d; }a[N2]; vector<ll> ve[N2*4]; vector<re> ve1[N2]; IL void arr(ll x,ll y,ll z) { a[++lx].a=head[x]; a[lx].b=y; a[lx].c=z; head[x]=lx; } void dfs1(ll x,ll y) { ll u=head[x]; //fa[x]=y; son[x]=-1; size[x]=1; dep[x]=dep[y]+1; while(u) { ll v=a[u].b; dis[v]=dis[x]+a[u].c; dfs1(v,x); bz1[0][v]=x; bz2[0][v]=a[u].c; //size[x]+=size[v]; //if (son[x]==-1||size[v]>size[son[x]]) son[x]=v; u=a[u].a; } } /* void dfs2(ll x,ll y) { top[x]=y; id[x]=++num; real2[num]=x; if (son[x]==-1) return; dfs2(son[x],y); ll u=head[x]; while (u) { ll v=a[u].b; if (v!=son[x]) dfs2(v,v); u=a[u].a; } } */ IL double check1(ll x,ll y) { return(1.00000000*(dp[x]-dp[y])/(dis[x]-dis[y])); } ll query(ll x,ll h,ll t,ll h1,ll t1,ll k) { if (h1<=h&&t<=t1) { if (!now[x]) return(INF); ll l=0,r=now[x]-1; while (l<r) { ll midd=(l+r)/2; if (check1(ve[x][midd+1],ve[x][midd])<=p[k]) l=midd+1; else r=midd; } ll xx=ve[x][l]; return(dp[xx]+p[k]*(dis[k]-dis[xx])+q[k]); } ll ans=INF; if (h1<=mid) ans=query(x*2,h,mid,h1,t1,k); if (mid<t1) ans=min(ans,query(x*2+1,mid+1,t,h1,t1,k)); return(ans); } IL bool check2(ll x,ll y,ll z) { if (1.00000000*(dp[z]-dp[x])/(dis[z]-dis[x])>=1.0000000*(dp[x]-dp[y])/(dis[x]-dis[y])) return(1); else return(0); } void change(ll x,ll h,ll t,ll pos,ll k) { ll l=0,r=now[x]-1; while (l<r) { ll midd=(l+r)/2; if (check2(ve[x][midd+1],ve[x][midd],k)) l=midd+1; else r=midd; } /*ve1[k].push_back((re){x,now[x],l+1,l+1<now[x]?ve[x][l+1]:-1}); if (!now[x]) now[x]=1; else now[x]=l+2; if (now[x]>ve[x].size()) ve[x].push_back(k); else ve[x][now[x]-1]=k; */ int tmp=now[x]; if (!now[x]) now[x]=1; else now[x]=l+2; if (now[x]>ve[x].size()) ve[x].push_back(k),ve1[k].push_back((re){x,tmp,-1,-1}); else ve1[k].push_back((re){x,tmp,now[x]-1,ve[x][now[x]-1]}),ve[x][now[x]-1]=k; if (h==t) return; if (pos<=mid) change(x*2,h,mid,pos,k); else change(x*2+1,mid+1,t,pos,k); } void dfs3(ll x) { ll ans=INF; /*vector<ll> ve2[N]; int now2[N]; for (int i=1;i<=n;i++) for (int j=1;j<=now[i];j++) cout<<ve[i][j-1]<<" "; cout<<" "<<x<<"a"<<endl; for (int i=1;i<=n;i++) for (int j=1;j<=now[i];j++) ve2[i].push_back(ve[i][j-1]),now2[i]=now[i]; */ ans=query(1,1,n,dep[l[x]],dep[x],x); if (x!=1) dp[x]=ans; else dp[x]=0; change(1,1,n,dep[x],x); ll u=head[x]; while (u) { ll v=a[u].b; dfs3(v); u=a[u].a; } rep(i,0,ve1[x].size()-1) { re xx=ve1[x][i]; now[xx.a]=xx.b; if (xx.d!=-1) { ve[xx.a][xx.c]=xx.d; } } /* for (int i=1;i<=n;i++) for (int j=1;j<=now[i];j++) cout<<ve[i][j-1]<<" "; cout<<" "<<x<<endl; */ /* for (int i=1;i<=n;i++) for (int j=1;j<=now[i];j++) if (ve[i][j-1]!=ve2[i][j-1]) cout<<now[i]<<" "<<now2[i]<<"false"<<endl;*/ } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); read(n); read(m); rep(i,2,n) { ll x,y; read(x); read(y); arr(x,i,y); read(p[i]); read(q[i]); read(l[i]); } dfs1(1,0); // dfs2(1,1); rep(i,1,19) rep(j,1,n) { bz1[i][j]=bz1[i-1][bz1[i-1][j]]; bz2[i][j]=bz2[i-1][j]+bz2[i-1][bz1[i-1][j]]; } rep(i,1,n) { ll ans=i; dep(j,19,0) if (l[i]>=bz2[j][ans]) l[i]-=bz2[j][ans],ans=bz1[j][ans]; l[i]=max(ans,1); } dfs3(1); rep(i,2,n) printf("%lld\n",dp[i]); return 0; }
另外一个做法是点分治
我觉得点分治挺有用的。。
首先点分治的一个结论是所有联通块的大小之和时nlogn级别的
然后我们取出重心之后,先做重心和根相连的这一块
然后对这些点建立凸包,用来更新其他点(点分治和cdq分治线段树分治这些都差不多,也就是利用了离线这一特性)
我们按照dep[i]-l[i]排序然后依次边建边询问
这样的复杂度是 nlogn(点数目)*logn(查询时间)
我记得apio的时候wys说这个有nlogn的做法 但我并不知道。。