题目大意:
给定一棵$n$个点并且有边权的树,每个点的权值为该点能走的最远长度,并输入$m$个询问,每次询问最多有多少个编号连续的点,他们的最大最小点权差小于等于$Q$。
思路:
两趟DP(DFS)求出每个点能走的最远长度,然后用ST算法预处理出每一段最大最小值。
对于每组询问,用尺取法求出最大值。
注意log2不能用cmath里面的函数(尤其是不能在for语句上),不然会TLE,最好是自己实现。
#include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
char ch;
while(!isdigit(ch=getchar()));
int x=ch^'';
while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
return x;
}
inline int log2(const float x){
return ((unsigned&)x>>&)-;
}
const int N=,logN=;
struct Edge {
int to,w;
};
std::vector<Edge> e[N];
inline void add_edge(const int u,const int v,const int w) {
e[u].push_back((Edge){v,w});
}
int w[N][],son[N];
void dfs1(const int x,const int par) {
w[x][]=w[x][]=;
for(unsigned i=;i<e[x].size();i++) {
int &y=e[x][i].to;
if(y==par) continue;
dfs1(y,x);
if(w[y][]+e[x][i].w>w[x][]) {
w[x][]=w[x][];
w[x][]=w[y][]+e[x][i].w;
son[x]=y;
}
else if(w[y][]+e[x][i].w>w[x][]) {
w[x][]=w[y][]+e[x][i].w;
}
}
}
void dfs2(const int x,const int par) {
for(unsigned i=;i<e[x].size();i++) {
int &y=e[x][i].to;
if(y==par) continue;
w[y][]=std::max(w[x][],son[x]!=y?w[x][]:w[x][])+e[x][i].w;
dfs2(y,x);
}
}
int max[N][logN],min[N][logN];
inline int getsum(const int l,const int r) {
int k=log2(r-l+);
return std::max(max[l][k],max[r-(<<k)+][k])-std::min(min[l][k],min[r-(<<k)+][k]);
}
int main() {
for(;;) {
int n=getint(),m=getint();
if(!n&&!m) return ;
for(int i=;i<n;i++) {
int u=getint(),v=getint(),w=getint();
add_edge(u,v,w);
add_edge(v,u,w);
}
dfs1(,);
dfs2(,);
for(int i=;i<=n;i++) {
max[i][]=min[i][]=std::max(w[i][],w[i][]);
}
for(int j=;<<j<=n;j++) {
for(int i=;i<=n-(<<j)+;i++) {
max[i][j]=std::max(max[i][j-],max[i+(<<(j-))][j-]);
min[i][j]=std::min(min[i][j-],min[i+(<<(j-))][j-]);
}
}
while(m--) {
int q=getint(),ans=;
for(int l=,r=;l<=r;l++) {
for(;r<=n&&getsum(l,r)<=q;r++);
ans=std::max(ans,r-l);
}
printf("%d\n",ans);
}
for(int i=;i<=n;i++) e[i].clear();
}
}