前不久搞了搞点分治,一直没有打模板练习,昨晚练习了一下(POJ1741),因为一个初始值赋成0x3f调了半小时,事实告诉我们,随手赋值0x3f3f3f3f
谈一下点分治是啥吧,点分治呢,其实个人感觉是对于求解树上路径问题的暴力优化:
对于一棵无根树,求解关于它上面路径的权值等问题均可以用点分治解决,但是需要有很好的理解及代码,分析能力。
点分治:
对于一棵无根树首先找到一个点作为它的根,因为随便拿一个做根求解复杂度过于高,近似(N^2)的复杂度,于是我们考虑一棵树的重心(重心指将这个点删去后,剩下的最大联通块包含的节点数最小),知道树的重心分割后的联通块最大节点数不超过N/2,于是我们可以用重心作为根,对于每次分割的联通块为一层,则最多有logN层,每一层查找复杂度为O(N),则总体复杂度近似O(NlogN),但是常数比较大,所以我认为是优化的暴力
找到根之后,我们对于根统计ans,我们知道对于一个点,只有两条路径,通过它的,不通过它的,由于分治,我们只要求计算通过它的,很容易知道通过根的肯定在两个不同的子树,但要排除在同一子树的情况(也就是不通过它的),我们可以先求出所有deep,然后求出根的ans,再对于它的子树求deep,然后减去他们对答案的贡献就行了,差不多就这样ans(root)-Σans(tree).
然后对于每一个子树求解下一层的根,继续进行点分治就OK了,那么例题拿一下JZOJ4715讲
题面:
给出一棵树,求出最小的k,使得,且在树中存在路径p,使得k>=S且k<=E。(k为路径p上的边的权值和)
输入
第一行给出N,S,E。N代表树的点数,S,E如题目描述。
下面N-1行给出这棵树的相邻两个节点的边及其权值W。
对于20%的数据满足n<=300
对于50%的数据满足n<=3000
对于60%的数据满足n<=10^5
对于以上数据,满足|E-S|<=50
对于100%的数据满足n<=10^5,|E-S|<=10^6
对于所有数据满足1<=Wi<=1000,|E|,|S|<=10^9
输出
输出共一行一个整数,表示答案。若无解输出-1。
样例输入 输出
5 10 40 16
2 4 80
2 3 57
1 2 16
2 5 49
那么对于题目进行剖析,关于树上路径权值,且是无根树,那么可以点分治解决,分析一下,要求我们找出树上一条路径的权值之和>=S 且 <=E 且权值最小,求最小,而且存在无解情况,由于最小,我们可以忽略上边界,以下边界为判定界限,然后就是点分治,比较模板了,对于每个重心的子树dfs,然后求出deep,排序后,l,r从两边开始跳,由于我们不能求在同一子树内的答案,于是在求解deep的时候我们记录一下每个点的属于哪个子树,如果所属子树相同,我们知道对于处于同一子树的,我们保证ans要小,所以判断一下它和r-1的距离是不是
# include<cstdio>
# include<algorithm>
# include<cstring>
using namespace std;
const int N = 2e5+10;
typedef long long ll;
struct b
{
int nex,to;
ll dis;
}e[N*2];
struct c
{
int f;
ll di;
}dep[N];
int n,root,i,tot,len,sum,s,E;
ll dis,d[N],ans;
int maxx[N],siz[N],st[N];
bool vis[N];
int cmp(c x,c y)
{
return x.di < y.di;
}
void add(int u,int v,int x)
{
e[++tot].to = v;
e[tot].nex = st[u];
st[u] = tot;
e[tot].dis = x;
}
void getrt(int x,int fa)
{
siz[x] = 1,maxx[x] = 0;
for (int i = st[x];i;i = e[i].nex)
{
int v = e[i].to;
if (v == fa || vis[v]) continue;
getrt(v,x);
siz[x] += siz[v];
maxx[x] = max(maxx[x],siz[v]);
}
maxx[x] = max(maxx[x],sum - siz[x]);
if (maxx[x] < maxx[root]) root = x;
}
void getdeep(int x,int fa,int zs)
{
dep[++len].di = d[x];
dep[len].f = zs;
for (int i = st[x];i;i = e[i].nex)
{
int v = e[i].to;
if (v == fa || vis[v]) continue;
d[v] = d[x] + e[i].dis;
if (!zs) getdeep(v,x,v);
else getdeep(v,x,zs);
}
}
ll cal(int x,int di)
{
len = 0,d[x] = di;
getdeep(x,0,0);
sort(dep + 1,dep + len + 1,cmp);
int l = 1,r = len; ll last = 0x3f3f3f3f;
while (l < r)
{
if (dep[l].f == dep[r].f)
{
if (dep[l].di + dep[r - 1].di < s) l++;
else r--;
}else
{
if (dep[l].di + dep[r].di >= s) { last = min(last,dep[l].di + dep[r].di); r--; }
else l++;
}
}
return last;
}
void solve(int x)
{
ans = min(ans,cal(x,0));
vis[x] = 1;
for (int i = st[x];i;i = e[i].nex)
{
int v = e[i].to;
if (vis[v]) continue;
sum = siz[v];
root = 0;
getrt(v,0);
solve(root);
}
}
int main()
{
scanf("%d%d%d",&n,&s,&E);
ans = 0x3f3f3f;
memset(vis,0,sizeof(vis));
memset(st,0,sizeof(st));
sum = n;
for (i = 1;i < n; i++)
{
int u,v,x;
scanf("%d%d%d",&u,&v,&x);
add(u,v,x),add(v,u,x);
}
maxx[0] = 0x3f3f3f3f;
getrt(1,0);
solve(root);
printf("%lld\n",ans > E ? -1 : ans);
return 0;
}