BZOJ2870 最长道路
Time Limit: 2 Sec Memory Limit: 512 MB
Description
\(H\)城很大,有\(N\)个路口(从\(1\)到\(N\)编号),路口之间有\(N-1\)边,使得任意两个路口都能互相到达,这些道路的长度我们视作一样。每个路口都有很多车辆来往,所以每个路口i都有一个拥挤程度\(v[i]\),我们认为从路口\(s\)走到路口\(t\)的痛苦程度为s到t的路径上拥挤程度的最小值,乘上这条路径上的路口个数所得的积。现在请你求出痛苦程度最大的一条路径,你只需输出这个痛苦程度。
简化版描述:
给定一棵\(N\)个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大。
其中链长度定义为链上点的个数。
Input
第一行\(N\)
第二行\(N\)个数分别表示\(1\)~\(N\)的点权v[i]
接下来\(N-1\)行每行两个数\(x,y\),表示一条连接\(x\)和\(y\)的边
Output
一个数,表示最大的痛苦程度。
Sample Input
3
5 3 5
1 2
1 3
Sample Output
10
【样例解释】
选择从\(1\)到\(3\)的路径,痛苦程度为\(min(5,5)\times 2=10\)
HINT
\(100\%\)的数据\(n\leq50000\)
其中有\(20\%\)的数据树退化成一条链
所有数据点权\(\leq65536\)
Hint:建议答案使用\(64\)位整型
Solution 1
这道题,所谓的链,和点对其实是相通的,所以可用点分治。
统计过\(rt\)的链时,对于每个子树,统计从根到里面每个点\(x\)的路径上最小值\(dist[x]\),然后和之前的子树里面的路径最小值\(\geq dist[x]\)的点中离根最远的形成链更新答案。然后之前的子树里面的路径最小值\(\geq dist[x]\)的点中离根最远的用树状数组维护后缀最大值就可以了。
这个就是点分治的思路,听说还可以用边分治,但是我不太会,反正都是树分治嘛,以后学习一下。
这个算法时间复杂度\(O(n\log^2n)\)。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
#define REP(i,a,n) for(register int i=(a);i<=(n);++i)
#define PER(i,a,n) for(register int i=(a);i>=(n);--i)
#define FEC(i,x) for(register int i=head[x];i;i=g[i].ne)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
namespace io{
const int SIZE=(1<<21)+1;char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=obuf,*oT=oS+SIZE-1,c,qu[55];int f,qr;
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),(iS==iT?EOF:*iS++)):*iS++)
inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
inline void putc(char x){*oS++=x;if(oS==oT)flush();}
template<class I>inline void read(I &x){for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;}
template<class I>inline void write(I x){if(!x)putc('0');if(x<0)putc('-'),x=-x;while(x)qu[++qr]=x%10+'0',x/=10;while(qr)putc(qu[qr--]);}
inline void print(const char *s){while(*s!='\0')putc(*s++);}
struct Flusher_{~Flusher_(){flush();}}io_flusher_;
}//orz laofudasuan
using io::read;using io::putc;using io::write;using io::print;
typedef long long ll;typedef unsigned long long ull;
template<typename A,typename B>inline bool SMAX(A&x,const B&y){return x<y?x=y,1:0;}
template<typename A,typename B>inline bool SMIN(A&x,const B&y){return y<x?x=y,1:0;}
const int N=50000+7,M=65536+7;
int n,m,a[N],x,y,mima,sum,rt,num[N],vis[N];ll ans;
struct Edge{int to,ne;}g[N<<1];int head[N],tot;
inline void Addedge(int x,int y){g[++tot].to=y;g[tot].ne=head[x];head[x]=tot;}
int s[M];
inline void Add(int x,int k){while(x)SMAX(s[x],k),x-=lowbit(x);SMAX(s[0],k);}
inline void Clear(int x){while(x)s[x]=0,x-=lowbit(x);s[0]=0;}
inline int Query(int x){if(!x)return s[0];int ans=0;while(x<=m)SMAX(ans,s[x]),x+=lowbit(x);return ans;}//错误笔记:树状数组一定要注意的地方:0!!!!!一定要特判!!!
inline void Getroot(int x,int fa=0){
num[x]=1;int f=0;
for(register int i=head[x];i;i=g[i].ne){
int y=g[i].to;if(vis[y]||y==fa)continue;
Getroot(y,x);num[x]+=num[y];SMAX(f,num[y]);
}SMAX(f,sum-num[x]);if(SMIN(mima,f))rt=x;
}
int q[N],hd,tl,f[N],dep[N],dist[N];
inline void BFS(int x,int rt){
q[hd=0,tl=1]=x;f[x]=rt;
while(hd<tl){//dbg("*");
x=q[++hd];int p=Query(dist[x]);if(p>0)SMAX(ans,(ll)dist[x]*(p+dep[x]-1));
for(register int i=head[x];i;i=g[i].ne){
int y=g[i].to;if(y==f[x]||vis[y])continue;
dist[y]=min(dist[x],a[y]);dep[y]=dep[x]+1;q[++tl]=y;f[y]=x;
}
}
}
int hkk[N],top,fst[N],ttop;
inline void Calc(int x){
Add(a[x],1);SMAX(ans,a[x]);top=0;
for(register int i=head[x];i;i=g[i].ne){
int y=g[i].to;if(vis[y])continue;hkk[++top]=y;
dep[y]=2,dist[y]=min(a[x],a[y]);BFS(y,x);num[y]=tl;
for(register int i=1;i<=tl;++i)Add(dist[q[i]],dep[q[i]]),fst[++ttop]=dist[q[i]];
}while(ttop)Clear(fst[ttop--]);Add(a[x],1);
while(top){
int y=hkk[top--];
dep[y]=2,dist[y]=min(a[x],a[y]);BFS(y,x);num[y]=tl;
for(register int i=1;i<=tl;++i)Add(dist[q[i]],dep[q[i]]),fst[++ttop]=dist[q[i]];
}while(ttop)Clear(fst[ttop--]);Clear(a[x]);
}
inline void Solve(int x){
vis[x]=1;Calc(x);
for(register int i=head[x];i;i=g[i].ne){
int y=g[i].to;if(vis[y])continue;
mima=sum=num[y];Getroot(y);Solve(rt);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("BZOJ2870.in","r",stdin);freopen("BZOJ2870.out","w",stdout);
#endif
read(n);for(register int i=1;i<=n;++i)read(a[i]),SMAX(m,a[i]);
for(register int i=1;i<n;++i)read(x),read(y),Addedge(x,y),Addedge(y,x);
mima=sum=n;Getroot(1);Solve(rt);write(ans),putc('\n');
}
Solution 2
两个\(\log\)太慢了?不急,我们还有一个的。
我们对点权从大到小排序,然后慢慢加边(加边用并查集维护,这个很套路),使得整个图中的点的最大值不超过目前的值。然后我们维护一下目前的直径(就是树中最长链),这个可以用树的直径的性质做出来:
切一刀把树分成联通两的部分,那么原来树的直径一定在分成的两个部分的直径的四个顶点之间。(这个在之前一篇校内题解也有提及:https://www.cnblogs.com/hankeke/p/20181024-find.html)
那么我们可以对两个树形联通快合并起来以后可以根据原来直径的四个顶点来求出新直径。那么就可以直径合并了。
然后我们就用目前的直径乘一下直径即可。这题如果题解看不懂的话可以看看程序。时间复杂度\(O(n\log n)\)。但是合并时候常数比较大,没有比两个\(\log\)快多少。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
#define REP(i,a,n) for(register int i=(a);i<=(n);++i)
#define PER(i,a,n) for(register int i=(a);i>=(n);--i)
#define FEC(i,x) for(register int i=head[x];i;i=g[i].ne)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
namespace io{
const int SIZE=(1<<21)+1;char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=obuf,*oT=oS+SIZE-1,c,qu[55];int f,qr;
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),(iS==iT?EOF:*iS++)):*iS++)
inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
inline void putc(char x){*oS++=x;if(oS==oT)flush();}
template<class I>inline void read(I &x){for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;}
template<class I>inline void write(I x){if(!x)putc('0');if(x<0)putc('-'),x=-x;while(x)qu[++qr]=x%10+'0',x/=10;while(qr)putc(qu[qr--]);}
inline void print(const char *s){while(*s!='\0')putc(*s++);}
struct Flusher_{~Flusher_(){flush();}}io_flusher_;
}//orz laofudasuan
using io::read;using io::putc;using io::write;using io::print;
typedef long long ll;typedef unsigned long long ull;
template<typename A,typename B>inline bool SMAX(A&x,const B&y){return x<y?x=y,1:0;}
template<typename A,typename B>inline bool SMIN(A&x,const B&y){return y<x?x=y,1:0;}
const int N=50000+7,LOG=18;
int n,x,y,lg[N<<1];ll ans=0;
int dfc,dfn[N],seq[N<<1],lca[N<<1][LOG],dep[N],vis[N];
struct Point{int val,id;inline bool operator<(const Point&a)const{return val>a.val;};}a[N];
struct Edge{int to,ne;}g[N<<1];int head[N],tot;
inline void Addedge(int x,int y){g[++tot].to=y;g[tot].ne=head[x];head[x]=tot;}
inline void DFS(int x,int fa=0){
dep[x]=dep[fa]+1;seq[++dfc]=x;dfn[x]=dfc;
for(register int i=head[x];i;i=g[i].ne)if(g[i].to!=fa)DFS(g[i].to,x),seq[++dfc]=x;
}
inline void RMQ_init(){
for(register int i=1;i<=dfc;++i)lca[i][0]=seq[i];
for(register int j=1;(1<<j)<=dfc;++j)
for(register int i=1;i+(1<<j)-1<=dfc;++i){
int a=lca[i][j-1],b=lca[i+(1<<(j-1))][j-1];
lca[i][j]=dep[a]<dep[b]?a:b;
}
}
inline int Qmin(int l,int r){int k=lg[r-l+1],a=lca[l][k],b=lca[r-(1<<k)+1][k];return dep[a]<dep[b]?a:b;}
inline int LCA(int x,int y){return dfn[x]<dfn[y]?Qmin(dfn[x],dfn[y]):Qmin(dfn[y],dfn[x]);}
inline int Dist(int x,int y){return dep[x]+dep[y]-(dep[LCA(x,y)]<<1)+1;}
int fa[N];
inline int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);}
inline void Union(int x,int y){x=Find(x),y=Find(y);fa[y]=x;}
struct Node{int x,y;}t[N];
inline Node Merge(Node a,Node b){
int dist=Dist(a.x,a.y);Node p=a;
if(SMAX(dist,Dist(b.x,b.y)))p=b;
if(a.x!=b.y&&b.x!=a.y&&SMAX(dist,Dist(a.x,b.x)))p=Node{a.x,b.x};
if(a.y!=b.y&&a.x!=b.x&&SMAX(dist,Dist(a.x,b.y)))p=Node{a.x,b.y};
if(a.x!=b.x&&a.y!=b.y&&SMAX(dist,Dist(a.y,b.x)))p=Node{a.y,b.x};
if(a.y!=b.x&&a.x!=b.y&&b.x!=b.y&&b.x!=a.y&&a.x!=a.y&&SMAX(dist,Dist(a.y,b.y)))p=Node{a.y,b.y};
return p;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("BZOJ2870.in","r",stdin);freopen("BZOJ2870.out","w",stdout);
#endif
read(n);lg[1]=0;for(register int i=2;i<=(n<<1);++i)lg[i]=lg[i>>1]+1;
for(register int i=1;i<=n;++i)read(a[i].val),a[i].id=i,fa[i]=i,t[i]=Node{i,i};
for(register int i=1;i<n;++i)read(x),read(y),Addedge(x,y),Addedge(y,x);
DFS(1);RMQ_init();
sort(a+1,a+n+1);for(register int i=1;i<=n;++i){
for(register int j=head[a[i].id];j;j=g[j].ne)if(vis[g[j].to]){//错误笔记:把g[j].to打成g[i].to 而且一定要判断是否vis过
int x=Find(a[i].id),y=Find(g[j].to);if(x==y)continue;
t[x]=Merge(t[x],t[y]);Union(x,y);
}vis[a[i].id]=1;
SMAX(ans,(ll)Dist(t[Find(a[i].id)].x,t[Find(a[i].id)].y)*a[i].val);
}
write(ans),putc('\n');
}