这题是吴老师推荐的,于是我就去做了。
根据题意,在完成最大生成树后,对于树上从x到y的一条路径,求出最大的ck-cj(j<=k,ci为路径上第i个点的权值)。
我一开始的想法是二分,记路径xy的中点是mid,路径ab的答案记为ans(a,b),最大值为mx(a,b),最小值为mn(a,b),那么,ans(x,y)=max{ans(x,mid),ans(y,mid),mx(mid,y)-mn(x,mid)}。
但我忘记了一个问题,对于时间复杂度T(n)=2T(n/2)+logn,由主定理可知,解为T(n)=O(n)。换言之这并没有比暴力优越。
然而,正如同求路径上的最大最小值一样,我们可以通过倍增的方法把ans预处理出来,然后在O(logn)的时间内得到解。
具体说就是把一个点向上2^i的答案和一个点第2^i-1个祖先到i的答案都预处理出来。而预处理和最终计算答案的方法和上述的二分是完全一致的。
所以,我们可以通过O(n*logn)的时间完成预处理,再对与每个询问O(logn)地得到解。
时间复杂度O(n*logn+q*logn)。
1 #include <bits/stdc++.h> 2 using namespace std; 3 template <typename tp> inline void read(tp& x) 4 { 5 x=0; 6 char tmp=getchar();bool key=0; 7 for(;!((tmp>='0'&&tmp<='9')||tmp=='-');) tmp=getchar(); 8 if(tmp=='-') key=1,tmp=getchar(); 9 for(;tmp>='0'&&tmp<='9';) x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar(); 10 if(key) x=-x; 11 } 12 const int N=30010,M=50010,MAXPOS=15; 13 struct edge_for_buildtree{ 14 int a,b,v; 15 bool operator < (const edge_for_buildtree& x)const 16 { 17 return v>x.v; 18 } 19 }ed[M]; 20 struct edge_for_lca{ 21 int la,b; 22 }con[N<<1]; 23 int tot,fir[N]; 24 inline void add(int from,int to) 25 { 26 con[++tot].la=fir[from]; 27 con[tot].b=to; 28 fir[from]=tot; 29 } 30 int n,m,q,val[N],flag[N],anc[N][MAXPOS+5],mn[N][MAXPOS+5],mx[N][MAXPOS+5],deep[N]; 31 int recu[N][MAXPOS+5],recd[N][MAXPOS+5]; 32 inline void init() 33 { 34 memset(anc,0,sizeof anc); 35 memset(mn,0x3f,sizeof mn); 36 memset(mx,0,sizeof mx); 37 memset(fir,0,sizeof fir); 38 memset(recu,0,sizeof recu); 39 memset(recd,0,sizeof recd); 40 tot=0; 41 } 42 int get_flag(int pos) 43 { 44 return flag[pos]==pos? pos : flag[pos]=get_flag(flag[pos]); 45 } 46 inline void kruskal() 47 { 48 int x,y,ans=0,cnt=0; 49 sort(ed+1,ed+m+1); 50 for(register int i=1;i<=n;++i) flag[i]=i; 51 for(register int i=1;i<=m;++i) 52 { 53 x=ed[i].a;y=ed[i].b; 54 x=get_flag(x);y=get_flag(y); 55 if(x==y) continue; 56 add(ed[i].a,ed[i].b); 57 add(ed[i].b,ed[i].a); 58 ans+=ed[i].v; 59 flag[x]=y; 60 if(++cnt==n-1) break; 61 } 62 printf("%d ",ans); 63 } 64 void dfs_init(int pos) 65 { 66 deep[pos]=deep[anc[pos][0]]+1; 67 mn[pos][0]=mx[pos][0]=val[pos]; 68 recu[pos][0]=recd[pos][0]=0; 69 for(int i=1;i<=MAXPOS;++i) 70 { 71 anc[pos][i]=anc[anc[pos][i-1]][i-1]; 72 mn[pos][i]=min(mn[pos][i-1],mn[anc[pos][i-1]][i-1]); 73 mx[pos][i]=max(mx[pos][i-1],mx[anc[pos][i-1]][i-1]); 74 recu[pos][i]=max(max(recu[pos][i-1],recu[anc[pos][i-1]][i-1]),mx[anc[pos][i-1]][i-1]-mn[pos][i-1]); 75 recd[pos][i]=max(max(recd[pos][i-1],recd[anc[pos][i-1]][i-1]),mx[pos][i-1]-mn[anc[pos][i-1]][i-1]); 76 if(anc[pos][i]==0) break; 77 } 78 for(int i=fir[pos];i;i=con[i].la) 79 { 80 if(con[i].b==anc[pos][0]) continue; 81 anc[con[i].b][0]=pos; 82 dfs_init(con[i].b); 83 } 84 } 85 inline int lca(int x,int y) 86 { 87 if(deep[x]<deep[y]) swap(x,y); 88 for(int i=MAXPOS;i>=0;--i) 89 { 90 if(deep[anc[x][i]]>=deep[y]) x=anc[x][i]; 91 } 92 if(x==y) return x; 93 for(int i=MAXPOS;i>=0;--i) 94 { 95 if(anc[x][i]!=anc[y][i]) 96 x=anc[x][i],y=anc[y][i]; 97 } 98 return anc[x][0]; 99 } 100 inline int get_mn(int x,int y)//y is the ancestor of x 101 { 102 if(x==y) return val[x]; 103 int res=val[x],len=deep[x]-deep[y]+1; 104 for(int i=MAXPOS;i>=0;--i) 105 { 106 if(len>=(1<<i)) 107 res=min(res,mn[x][i]),x=anc[x][i],len-=(1<<i); 108 } 109 return res; 110 } 111 inline int get_mx(int x,int y)//y is the ancestor of x 112 { 113 if(x==y) return val[x]; 114 int res=val[x],len=deep[x]-deep[y]+1; 115 for(int i=MAXPOS;i>=0;--i) 116 { 117 if(len>=(1<<i)) 118 res=max(res,mx[x][i]),x=anc[x][i],len-=(1<<i); 119 } 120 return res; 121 } 122 inline int ask_mn(int x,int y,int lc) 123 { 124 return min(get_mn(x,lc),get_mn(y,lc)); 125 } 126 inline int ask_mx(int x,int y,int lc) 127 { 128 return max(get_mx(x,lc),get_mx(y,lc)); 129 } 130 inline int get_recu(int x,int y)//y is the ancestor of x 131 { 132 if(x==y) return 0; 133 int mnpre=1000000,res=0; 134 for(int i=MAXPOS;i>=0;i--) 135 { 136 if(deep[anc[x][i]]>=deep[y]) 137 { 138 res=max(res,recu[x][i]); 139 res=max(res,mx[x][i]-mnpre); 140 mnpre=min(mnpre,mn[x][i]); 141 x=anc[x][i]; 142 } 143 } 144 return res; 145 } 146 inline int get_recd(int x,int y) 147 { 148 if(x==y) return 0; 149 int mxpre=0,res=0; 150 for(int i=MAXPOS;i>=0;i--) 151 { 152 if(deep[anc[x][i]]>=deep[y]) 153 { 154 res=max(res,recd[x][i]); 155 res=max(res,mxpre-mn[x][i]); 156 mxpre=max(mxpre,mx[x][i]); 157 x=anc[x][i]; 158 } 159 } 160 return res; 161 } 162 inline int solve(int x,int y) 163 { 164 if(x==y) return 0; 165 if(anc[x][0]==y||anc[y][0]==x) return max(0,val[y]-val[x]); 166 int z=lca(x,y); 167 int res=max(get_recu(x,z),get_recd(y,z)); 168 res=max(res,ask_mx(y,z,z)-ask_mn(x,z,z)); 169 return res; 170 } 171 int main() 172 { 173 int x,y,v; 174 while(scanf("%d",&n)!=EOF) 175 { 176 init(); 177 for(register int i=1;i<=n;++i) read(val[i]); 178 read(m); 179 for(register int i=1;i<=m;++i) 180 { 181 read(x);read(y);read(v); 182 ed[i]=(edge_for_buildtree){x,y,v}; 183 } 184 kruskal(); 185 dfs_init(1); 186 read(q); 187 for(;q--;) 188 { 189 read(x);read(y); 190 printf("%d ",solve(x,y)); 191 } 192 } 193 return 0; 194 }
小结:通常对于无修改的题目,这种看似无用的二分往往可以通过倍增来优化。