题目:BZOJ4326、洛谷P2680、Vijos P1983、UOJ#150、codevs4632、codevs5440。

题目大意:有一棵带权树,有一些运输计划,第i个运输计划从ai到bi,耗时为ai到bi的距离,所有运输计划一起开始。现在可以把一条边权变成0,求最终运输计划最短要多少时间。

解题思路:标算是树剖,然而我并不会……

我的方法是LCA+二分+树上差分。

首先LCA,求出每个运输计划的长度,可一遍dfs求出每个节点到根的距离dist,则a到b的长度为dist[a]+dist[b]-(dist[lca(a,b)]<<1)。

接着二分答案,然后判断答案可行性。

对于每一个答案,我们要找的是所有长度大于当前答案的运输计划的公共边,因为只有所有长度大于当前答案的运输计划全部变成小于等于当前答案,当前答案才可行。

用树上差分(不懂请百度)。我们用s[i]记录i到它父亲这条边有多少计划经过。

对于每个运输计划,如果长度大于当前答案,我们给s[a]+1,s[b]+1,s[lca(a,b)]-2,因为我们要统计的是边,所以对于两个点,lca(a,b)对应的边实际是没有走到的,所以-2。

差分完后判断答案可行性即可。

我用倍增时间复杂度为$O(m\log_2 n+(n+m)\log_2 len)$,len为运输计划的最大长度。

但常数巨大,1s很容易被卡,因此有些地方还过不掉。例如我在UOJ上就被卡97。但在时限较宽或数据较弱的地方还是能AC的。

C++ Code:

%:pragma GCC optimize(2)
#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
char buf[10700005];
int bufpos,n,m,head[300001],deep[300001],p[300001][21],dist[300001],s[300001],fa_edge[300001],mx,now;
struct edge{
int to,dis,nxt;
}e[600003];
struct que{
int a,b,len,LCA;
}f[300001];
inline int max(int a,int b){return(a>b)?a:b;}
inline int readint(){
char c=buf[bufpos++];
for(;!isdigit(c);c=buf[bufpos++]);
int p=0;
for(;isdigit(c);c=buf[bufpos++])p=(p<<3)+(p<<1)+(c^'0');
return p;
}
void dfs(int s){
for(int i=head[s];i;i=e[i].nxt)
if(!deep[e[i].to]){
fa_edge[e[i].to]=i;
deep[e[i].to]=deep[s]+1;
p[e[i].to][0]=s;
dist[e[i].to]=dist[s]+e[i].dis;
dfs(e[i].to);
}
}
int lca(int x,int y){
if(deep[x]<deep[y])x^=y^=x^=y;
int i;
for(i=0;(1<<i)<=deep[x];++i);--i;
for(int j=i;j>-1;--j)
if(deep[p[x][j]]>=deep[y])x=p[x][j];
if(x==y)return x;
for(int j=i;j>-1;--j)
if(p[x][j]!=-1&&p[x][j]!=p[y][j])x=p[x][j],y=p[y][j];
return p[x][0];
}
void updata(int now){
for(int i=head[now];i;i=e[i].nxt)
if(deep[now]<deep[e[i].to]){
updata(e[i].to);
s[now]+=s[e[i].to];
}
}
bool ok(int ans){
memset(s,0,sizeof s);
int gz=0;
for(int i=1;i<=n;++i)
if(f[i].len>ans){
++gz;
++s[f[i].a];
++s[f[i].b];
s[f[i].LCA]-=2;
}
updata(1);
for(int i=1;i<=m;++i)
if(s[i]==gz&&mx-ans<=e[fa_edge[i]].dis)return true;
return false;
}
int main(){
buf[fread(buf,1,10700000,stdin)]=bufpos=0;
m=readint(),n=readint();
for(int i=1;i<m;++i){
int from=readint(),to=readint(),dis=readint();
now=i<<1;
e[now-1]=(edge){to,dis,head[from]};
head[from]=now-1;
e[now]=(edge){from,dis,head[to]};
head[to]=now;
}
memset(p,-1,sizeof p);
dist[1]=0;
deep[1]=1;
dfs(1);
for(int j=1;(1<<j)<=m;++j)
for(int i=1;i<=m;++i)
if(p[i][j-1]!=-1)p[i][j]=p[p[i][j-1]][j-1];
int l=0,r=0,mid;
for(int i=1;i<=n;++i){
f[i].a=readint();
f[i].b=readint();
f[i].LCA=lca(f[i].a,f[i].b);
r=max(r,f[i].len=dist[f[i].a]+dist[f[i].b]-(dist[f[i].LCA]<<1));
}
mx=r;
++r;
while(l<r){
mid=l+r>>1;
if(ok(mid))r=mid;else
l=mid+1;
}
printf("%d\n",r);
return 0;
}

倍增时间复杂度比较高,我改成用Tarjan求LCA,速度瞬间变快,在UOJ上成功卡了过去。不过codevs4632仍然被卡。

以下为Tarjan代码。

C++ Code:

%:pragma GCC optimize(2)
#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
char buf[10700005];
int bufpos,n,m,head[300001],deep[300001],dist[300001],s[300001],fa_edge[300001],mx,now,headq[300001],nq=0;
bool vis[300001];
int fa[300001];
struct edge{
int to,dis,nxt;
}e[600003];
struct que{
int a,b,len,LCA;
}f[300001];
struct Query{
int same,nxt,to,num;
bool flag;
}q[600003];
int find(int x){return(fa[x]==x)?x:(fa[x]=find(fa[x]));}
inline int max(int a,int b){return(a>b)?a:b;}
inline int readint(){
char c=buf[bufpos++];
for(;!isdigit(c);c=buf[bufpos++]);
int p=0;
for(;isdigit(c);c=buf[bufpos++])p=(p<<3)+(p<<1)+(c^'0');
return p;
}
void dfs(int s){
for(int i=head[s];i;i=e[i].nxt)
if(!deep[e[i].to]){
fa_edge[e[i].to]=i;
deep[e[i].to]=deep[s]+1;
dist[e[i].to]=dist[s]+e[i].dis;
dfs(e[i].to);
}
}
void tarjan(int root){
for(int i=head[root];i;i=e[i].nxt)
if(deep[root]<deep[e[i].to]){
tarjan(e[i].to);
fa[e[i].to]=root;
vis[e[i].to]=true;
}
for(int i=headq[root];i;i=q[i].nxt)
if(vis[q[i].to]&&!q[i].flag){
q[i].flag=q[q[i].same].flag=true;
f[q[i].num].LCA=find(q[i].to);
}
}
void updata(int now){
for(int i=head[now];i;i=e[i].nxt)
if(deep[now]<deep[e[i].to]){
updata(e[i].to);
s[now]+=s[e[i].to];
}
}
bool ok(int ans){
memset(s,0,sizeof s);
int gz=0;
for(int i=1;i<=n;++i)
if(f[i].len>ans){
++gz;
++s[f[i].a];
++s[f[i].b];
s[f[i].LCA]-=2;
}
updata(1);
for(int i=1;i<=m;++i)
if(s[i]==gz&&mx-ans<=e[fa_edge[i]].dis)return true;
return false;
}
int main(){
buf[fread(buf,1,10700000,stdin)]=bufpos=0;
m=readint(),n=readint();
for(int i=1;i<m;++i){
int from=readint(),to=readint(),dis=readint();
now=i<<1;
e[now-1]=(edge){to,dis,head[from]};
head[from]=now-1;
e[now]=(edge){from,dis,head[to]};
head[to]=now;
fa[i]=i;
}
fa[m]=m;
deep[1]=1;
dfs(1);
int l=0,r=0,mid;
for(int i=1;i<=n;++i){
f[i].a=readint();
f[i].b=readint();
int& x=f[i].a,&y=f[i].b;
q[++nq].to=y;
q[nq].same=nq+1;
q[nq].num=i;
q[nq].nxt=headq[x];
headq[x]=nq;
q[++nq].to=x;
q[nq].same=nq-1;
q[nq].num=i;
q[nq].nxt=headq[y];
headq[y]=nq;
if(x==y)
q[nq].flag=q[nq-1].flag=true,f[i].LCA=x;
}
tarjan(1);
for(int i=1;i<=n;++i)
r=max(r,f[i].len=dist[f[i].a]+dist[f[i].b]-(dist[f[i].LCA]<<1));
mx=r;
++r;
while(l<r){
mid=l+r>>1;
if(ok(mid))r=mid;else
l=mid+1;
}
printf("%d\n",r);
return 0;
}

最新文章

  1. NodeJS使用formidable实现文件上传
  2. Dynamo分布式系统——「RWN」协议解决多备份数据如何读写来保证数据一致性,而「向量时钟」来保证当读取到多个备份数据的时候,如何判断哪些数据是最新的这种情况
  3. 古诗词api,诗词接口,诗词api,中国诗词
  4. LeetCode() Min Stack 不知道哪里不对,留待。
  5. ExtJS笔记5 Components
  6. paper 45:latex的使用
  7. WPF 之 自定义窗体标题栏
  8. 《Python CookBook2》 第一章 文本 - 去字符串两端的空格 &amp;&amp; 合并字符串 &amp;&amp; 将字符串逐字符或者逐词反转
  9. gzip命令
  10. Rect
  11. [转]详解AppDelegate/UIApplication
  12. 【Beta】 第三次Daily Scrum Meeting
  13. Bootstrap的核心——栅格系统的使用
  14. 【转】搭建spark环境 单机版
  15. 正则表达式(Regular expressions)使用笔记
  16. python cook 整理
  17. Qt学习——QListWidget控件的使用
  18. 【转】结构struct 联合Union和枚举Enum的细节讨论
  19. JAVA 删除指定目录下指定文件类型的所有文件
  20. IIS 无法显示网页问题

热门文章

  1. WPF 样式
  2. .NET深入解析LINQ框架1
  3. Description Resource Path Location Type Cannot change version of project fac(导入maven项目出现红叉问题)
  4. 利用js自带函数 数组去重
  5. LightOJ-1138 Trailing Zeroes (III) 唯一分解定理 算n!的某个因数个数
  6. HDFS架构与原理
  7. 洛谷 P1169 [ZJOI2007]棋盘制作 (悬线法)
  8. 洛谷P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold
  9. 【POJ 3714】Raid
  10. sqoop从mysql导入到hdfs出现乱码问题