Problem
既然是NOIP的题目,那么……
意料之中的偷懒不可避免
Solution
Actually,刚开始看这道题目的时候没有什么想法,然后就手贱点开了标签——树链剖分!!!
于是我果断就怂了,过了几天,我现在又才继续写,发现好像也不一定需要树链剖分,不过树链剖分比较好写吧可能。树链剖分我只是看了看,也没有实现了,好像就是将树分成几条链,这样就可以在链上用线段树快速维护出需要的信息。当然,我还是先不谈论这种高深的话题了。
如果等我学了树链剖分之后,我还记得这件事的话,我就填坑。
首先,我们发现可以枚举这个最短距离k,而且还可以二分答案。我经过卡评测之后发现maxr设到
2∗108
2
∗
10
8
就可以A了。
其次为了好查询距离,我们可以记录每个节点到根节点的距离,再记录每组运输计划两节点的公共祖先,然后就可以利用
sum[x]+sum[y]−2∗sum[lca]
s
u
m
[
x
]
+
s
u
m
[
y
]
−
2
∗
s
u
m
[
l
c
a
]
快速地算出距离。找出距离大于k的计划,然后为了使它们的路径距离变小,应该都需要建立虫洞,那么最优当然就是建立在它们的公共交点上,枚举这些交点即可判断是否可行。
所以关键问题就在于如何快速求这个公共交点。对于一个距离超过k的运输计划x->y,记录路径对节点的覆盖次数,利用差分快速修改,
cha[x]+=1;cha[y]+=1;cha[lca]−=2
c
h
a
[
x
]
+
=
1
;
c
h
a
[
y
]
+
=
1
;
c
h
a
[
l
c
a
]
−
=
2
。最后在重新dfs一遍就可以获得每个点的覆盖次数。由于虫洞只能改某条航道,那就记录maxn为最长可能减少的距离,再与最长的路径maxdis相比较,如果减去之后仍然大于枚举出的k,那么显然不成立,否则就可行。依次来二分答案。
貌似这道题还需要一点卡常技巧,我也不知道考NOIP的时候,能不能卡进去,但至少交到OJ上A了。
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int size=300010,maxr=200000000;
struct data{
int v,w,nxt;
}edge[size<<1];
int n,m,p,head[size],deep[size],pre[size][21];
int s[size],t[size],lca[size],cha[size],sum[size];
template <typename Tp> inline void read(Tp &x)
{
x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
inline int max(int x,int y){return x>y?x:y;}
inline void insert(int u,int v,int w)
{
edge[++p].v=v;edge[p].w=w;
edge[p].nxt=head[u];head[u]=p;
edge[++p].v=u;edge[p].w=w;
edge[p].nxt=head[v];head[v]=p;
}
void dfs(int x,int fa)
{
for(int i=1;i<=20;i++)
pre[x][i]=pre[pre[x][i-1]][i-1];
for(int i=head[x];i;i=edge[i].nxt)
if(edge[i].v!=fa)
{
deep[edge[i].v]=deep[x]+1;
pre[edge[i].v][0]=x;
sum[edge[i].v]=sum[x]+edge[i].w;
dfs(edge[i].v,x);
}
}
int getlca(int x,int y)
{
if(deep[x]>deep[y])
swap(x,y);
int t=deep[y]-deep[x];
for(int i=20;i>=0;i--)
if(t&(1<<i))
y=pre[y][i];
if(x==y)
return x;
for(int i=20;i>=0;i--)
if(pre[x][i]!=pre[y][i])
x=pre[x][i],y=pre[y][i];
return pre[x][0];
}
void input()
{
int tu,tv,tw;
read(n),read(m);
for(int i=1;i<n;i++)
{
read(tu),read(tv),read(tw);
insert(tu,tv,tw);
}
dfs(1,0);
for(int i=1;i<=m;i++)
{
read(s[i]),read(t[i]);
lca[i]=getlca(s[i],t[i]);
}
}
void update(int x,int fa)
{
for(int i=head[x];i;i=edge[i].nxt)
if(edge[i].v!=fa)
{
update(edge[i].v,x);
cha[x]+=cha[edge[i].v];
}
}
bool check(int k)
{
memset(cha,0,sizeof(cha));
int num=0,maxn=0,maxdis=0;
for(int i=1;i<=m;i++)
if(sum[s[i]]+sum[t[i]]-2*sum[lca[i]]>k)
{
num++;
maxdis=max(maxdis,sum[s[i]]+sum[t[i]]-2*sum[lca[i]]);
cha[s[i]]++,cha[t[i]]++,cha[lca[i]]-=2;
}
update(1,0);
for(int i=2;i<=n;i++)
if(cha[i]>=num)
maxn=max(maxn,sum[i]-sum[pre[i][0]]);
if(maxdis-maxn>k)
return false;
return true;
}
int main()
{
int l=0,r=maxr,m;
input();
while(l<r)
{
m=(l+r)>>1;
if(check(m))
r=m;
else
l=m+1;
}
printf("%d\n",l);
return 0;
}