版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88253384
以前看着这个芳菲菲其弥章的题面以为这题又是什么数据结构神题。。。。。。
其实就是 加点横坐标单调但查询斜率不单调的斜率优化 + 时间限制 + 树上查询。
解法1(套路解法):
因为时间限制,我们用线段树维护每个小区间的凸包,最后求个最小值就行。
因为树上查询所以树链剖分。
因为这些操作常数都不是很大,时间复杂度分析出来也没啥意思。。。。
AC Code:
#include<bits/stdc++.h>
#define maxn 200005
#define inf 0x3f3f3f3f3f3f3f3f
#define LL long long
using namespace std;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct && (ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &res)
{
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=ch-'0'+res*10);
}
int n,t;
int info[maxn],Prev[maxn],to[maxn],cnt_e;
LL cst[maxn];
inline void Node(int u,int v,LL ct){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=ct; }
LL dep[maxn],p[maxn],q[maxn],l[maxn];
int fa[maxn],tp[maxn],id[maxn],bl[maxn],siz[maxn],son[maxn],tot,cnt_l;
void dfs1(int now)
{
siz[now] = 1 , son[now] = -1;
for(int i=info[now];i;i=Prev[i])
{
dep[to[i]] = dep[fa[to[i]] = now] + cst[i];
dfs1(to[i]);
siz[now] += siz[to[i]];
if(son[now] == -1 || (siz[son[now]] < siz[to[i]]))
son[now] = to[i];
}
}
void dfs2(int now)
{
id[now] = ++tot;
if(son[now]!=-1) bl[son[now]] = bl[now] , dfs2(son[now]);
for(int i=info[now];i;i=Prev[i])
if(to[i]!=son[now])
{
tp[bl[to[i]] = ++cnt_l] = to[i];
dfs2(to[i]);
}
}
struct Point
{
LL x,y;
Point(LL x=0,LL y=0):x(x),y(y){}
double operator *(const Point &B)const
{
return (B.y-y) * 1.0 / (B.x-x);
}
};
bool flag = 0;
vector<Point>pt[maxn<<2];
LL solveseg(int now,LL K)
{
vector<Point>&p = pt[now];
if(p.size() == 0) return inf;
if(p.size() == 1) return -K*p[0].x+p[0].y;
int L = 1 , R = p.size()-1 , mid; // mid point
for(;L<R;)
{
mid = (L+R+1) >> 1;
if(p[mid]*p[mid-1] < K) L = mid;
else R = mid-1;
}
if(p[L]*p[L-1] >= K) L--;
return -K*p[L].x+p[L].y;
}
LL solvesegs(int now,int l,int r,int ql,int qr,LL K)
{
if(ql > r || qr < l) return inf;
if(ql <= l && r <= qr)
{
return solveseg(now,K);
}
int mid = (l+r) >> 1;
return min(solvesegs(now<<1,l,mid,ql,qr,K),solvesegs(now<<1|1,mid+1,r,ql,qr,K));
}
LL solvepath(int now,LL K,LL lim)
{
if(!now) return inf;
if(dep[now] < lim) return inf;
if(dep[tp[bl[now]]]<lim)
{
int L=id[tp[bl[now]]],R = id[now],mid;
for(;L<R;)
{
mid = (L+R) >> 1;
if(dep[mid]<lim)L=mid+1;
else R=mid;
}
return solvesegs(1,1,n,L,id[now],K);
}
return min(solvesegs(1,1,n,id[tp[bl[now]]],id[now],K),solvepath(fa[tp[bl[now]]],K,lim));
}
void insertseg(int now,Point pts)
{
vector<Point>&p = pt[now];
for(int siz=p.size();siz>=2 && pts*p[siz-1]<=p[siz-1]*p[siz-2];p.pop_back(),siz--);
p.push_back(pts);
}
void insertsegs(int now,int l,int r,int pos,Point pts)
{
insertseg(now,pts);
if(l==r) return;
int mid = (l+r) >> 1;
if(pos <= mid)insertsegs(now<<1,l,mid,pos,pts);
else insertsegs(now<<1|1,mid+1,r,pos,pts);
}
LL dp[maxn];
void solve(int now)
{
if(now == 8)
flag = 1;
if(now > 1)
{
dp[now] = solvepath(fa[now],p[now],dep[now]-l[now])+q[now]+dep[now]*p[now];
}
insertsegs(1,1,n,id[now],Point(dep[now],dp[now]));
if(now == 8)
flag = 0;
for(int i=info[now];i;i=Prev[i])
solve(to[i]);
}
int main()
{
read(n),read(t);
for(int i=2;i<=n;i++)
{
int f;LL s;
read(f),read(s),read(p[i]),read(q[i]),read(l[i]);
Node(f,i,s);
}
dfs1(1),tp[bl[1]=++cnt_l]=1,dfs2(1);
solve(1);
for(int i=2;i<=n;i++)
printf("%lld\n",dp[i]);
}
解法2:因为是树链问题,我们可以点分治(类比序列上将序列分成两半的CDQ分治)然后贡献,注意要让每段树链分成
条点分树上的树链,然后每条树链对可以贡献的点算贡献,取个Min就行。
AC Code:
#include<bits/stdc++.h>
#define maxn 400005
#define LL long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct) ? 0 : *cs++)
template<class T>inline void read(T &res)
{
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
int n,t;
int fa[maxn],info[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e;
LL p[maxn],q[maxn],l[maxn],cst[maxn],disfa[maxn],ans[maxn],dep[maxn];
inline void Node(int u,int v,LL ct){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=ct; }
bool vis[maxn],usd[maxn];
int siz[maxn],Min,rt;
bool flag = 0;
void frt(int now,int ff,int tsz)
{
int Max = tsz - siz[now];
for(int i=info[now];i;i=Prev[i])
if(!vis[to[i]] && to[i]!=ff)
frt(to[i],now,tsz) , Max = max(Max , siz[to[i]]);
if(rt == -1 || Max < Min)
rt = now , Min = Max;
}
int findrt(int now,int tsz)
{
Min = 0x3f3f3f3f , rt = -1;
frt(now,0,tsz);
return rt;
}
void ser1(int now,int ff)
{
siz[now] = 1;
for(int i=info[now];i;i=Prev[i])
if(!vis[to[i]] && to[i]!=ff)
{
ser1(to[i],now);
siz[now] += siz[to[i]];
}
}
int sq[maxn];
LL dis[maxn];
void ser2(int now,int ff,LL dist)
{
sq[++sq[0]] = now , dis[now] = l[now] - dist , siz[now] = 1;
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff && !vis[to[i]])
ser2(to[i],now,dist+cst[i]),
siz[now] += siz[to[i]];
}
inline bool cmp(const int &u,const int &v){ return dis[u] < dis[v]; }
struct Point
{
LL x,y;
Point(LL x=0,LL y=0):x(x),y(y){}
double operator *(const Point &B)const
{
return (B.y-y) * 1.0 / (B.x-x);
}
};
vector<Point>pt;
void Insert(Point pts)
{
for(int siz=pt.size();siz>=2 && pts*pt[siz-1]>=pt[siz-1]*pt[siz-2];pt.pop_back(),siz--);
pt.push_back(pts);
}
LL Query(LL K)
{
if(pt.size() == 0) return inf;
if(pt.size() == 1) return pt[0].y - K * pt[0].x;
int L = 1 , R = pt.size()-1 , mid;
for(;L<R;)
{
mid = (L+R+1) >> 1;
if(pt[mid] * pt[mid-1] >= K) L = mid;
else R = mid - 1;
}
if(pt[L] * pt[L-1] <= K) L--;
return pt[L].y - K * pt[L].x;
}
void Solve(int now,int lim)
{
flag = 0;
vis[now] = 1;
if(fa[now] && !vis[fa[now]])
{
ser1(fa[now],0);
Solve(findrt(fa[now],siz[fa[now]]),lim);
}
sq[0] = 0;
ser2(now,0,0);
sort(sq+1,sq+1+sq[0],cmp);
for(int i=1,j=fa[now];i<=sq[0];i++)
{
int u = sq[i];
for(;j!=lim && dep[now]-dep[j]<=dis[u];j=fa[j])
{
Insert(Point(dep[j],ans[j]));
}
ans[u] = min(ans[u] , Query(p[u])+q[u]+dep[u]*p[u]);
}
pt.clear();
for(int i=info[now];i;i=Prev[i])
if(!vis[to[i]])
{
Solve(findrt(to[i],siz[to[i]]),fa[now]);
}
}
void dfs(int now,int ff)
{
siz[now] = 1;
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff)
{
dep[to[i]] = dep[now] + cst[i];
dfs(to[i],now);
siz[now] += siz[to[i]];
}
}
int main()
{
read(n),read(t);
for(int i=2;i<=n;i++)
{
int f;LL s;
read(f),read(s),read(p[i]),read(q[i]),read(l[i]);
Node(f,i,s),Node(i,f,s);
fa[i] = f , disfa[i] = s;
}
memset(ans,0x3f,sizeof ans);
ans[1] = 0;
dfs(1,0);
flag = 1;
Solve(findrt(1,n),0);
for(int i=2;i<=n;i++)
printf("%lld\n",ans[i]);
}
听说还可以二进制分组。。。。。。