bzoj1999 先把树的直径求出来,从左往右枚举,对于当前位置i,找到满足限制并且最远的点j,当前位置最大值就是max(i~j区间内除直径外的子树路径长度最大值,1~i的长度,j~n的长度)

然而,对于树的直径有一个很有用的性质,1~i区间内除直径外的子树路径长度最大值必然不会比1~i的长度大,否则就不是直径了。j~n同理

所以可以把i~j区间转换成1~n区间,成为定值。求完树的直径后O(n)即可出解

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std; int n,S;
struct node
{
int x,y,d,next;
}a[];int len,last[];
void ins(int x,int y,int d)
{
len++;
a[len].x=x;a[len].y=y;a[len].d=d;
a[len].next=last[x];last[x]=len;
}
int L,ld,R,rd;
void getL(int x,int fr,int d)
{
if(L==||d>ld)L=x,ld=d;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fr)
getL(y,x,d+a[k].d);
}
}
int pre[];
void getR(int x,int fr,int d)
{
if(R==||d>rd)R=x,rd=d;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fr)
{
pre[y]=k;
getR(y,x,d+a[k].d);
}
}
}
bool v[];
int getmax(int x,int fr)
{
int mx=;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(v[y]==false&&y!=fr)
mx=max(mx,getmax(y,x)+a[k].d);
}
return mx;
} int ulen,u[],dis[];
int main()
{
// freopen("core.in","r",stdin);
// freopen("core.out","w",stdout);
int x,y,dd;
scanf("%d%d",&n,&S);
len=;memset(last,,sizeof(last));
for(int i=;i<n;i++)
{
scanf("%d%d%d",&x,&y,&dd);
ins(x,y,dd);ins(y,x,dd);
}
L=;getL(,,);
R=;getR(L,,); memset(v,false,sizeof(v));
ulen=;u[++ulen]=R;dis[ulen]=;v[R]=true;
int k=pre[R];
while(a[k].x!=L)
{
u[++ulen]=a[k].x;
dis[ulen]=a[k].d+dis[ulen-];
v[a[k].x]=true;
k=pre[a[k].x];
}
u[++ulen]=a[k].x;
dis[ulen]=a[k].d+dis[ulen-];
v[a[k].x]=true; int mx=,j=;
for(int i=;i<=ulen;i++)mx=max(mx,getmax(u[i],));
int mn=;
for(int i=;i<=ulen;i++)
{
while(j<ulen&&dis[j+]-dis[i]<=S)j++;
mn=min(mn,max(mx,max(dis[i],dis[ulen]-dis[j])));
}
printf("%d\n",mn);
return ;
}

bzoj1999

poj3417 环的思想很重要。对于一条非树边,可以理解成和树上路径构成一个环,若删掉一条树边,删掉当前这条非树边,树边能够选择的就是在x,y树上最短路的边。树上路径修改,采用树上差分的做法。此后,遍历每一条边,计算答案。对于只被1个非树边覆盖的树边,ans++,只被0个覆盖的,所有非树边都可以随意。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std; struct node
{
int x,y,next;
}a[];int len,last[];
void ins(int x,int y)
{
len++;
a[len].x=x;a[len].y=y;
a[len].next=last[x];last[x]=len;
}
int f[][],dep[],Bin[];
void dfs(int x)
{
for(int i=;dep[x]>=Bin[i];i++)f[i][x]=f[i-][f[i-][x]]; for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=f[][x])
{
f[][y]=x;
dep[y]=dep[x]+;
dfs(y);
}
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=;i>=;i--)
if(dep[x]-dep[y]>=Bin[i])x=f[i][x];
if(x==y)return x;
for(int i=;i>=;i--)
if(dep[x]>=Bin[i]&&f[i][x]!=f[i][y])x=f[i][x],y=f[i][y];
return f[][x];
} int ans,m,d[];
void getans(int x)
{
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=f[][x])
getans(y), d[x]+=d[y];
}
if(x!=)
{
if(d[x]==)ans+=m;
else if(d[x]==)ans++;
}
}
int main()
{
Bin[]=;for(int i=;i<=;i++)Bin[i]=Bin[i-]*;
int n,x,y;
scanf("%d%d",&n,&m);
len=;memset(last,,sizeof(last));
for(int i=;i<n;i++)
{
scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
dfs(); for(int i=;i<=m;i++)
{
scanf("%d%d",&x,&y);
d[x]++;d[y]++;
d[LCA(x,y)]-=;
} ans=;
getans();
printf("%d\n",ans); return ;
}

poj3417

CH Round #56-C 对于当前出现的异象石,按照dfs序排序,对于相邻的点求距离(包括首尾),答案就是总距离/2。动态维护这个序列,就是平衡树啊。操作简单,什么set啊,或者线段树啊奇淫维护就好

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
typedef long long LL; struct node
{
int x,y,next;LL d;
}a[];int len,last[];
void ins(int x,int y,LL d)
{
len++;
a[len].x=x;a[len].y=y;a[len].d=d;
a[len].next=last[x];last[x]=len;
}
int Bin[];
int f[][],dep[];
int z,ys[];LL d[];
void dfs(int x)
{
for(int i=;dep[x]>=Bin[i];i++)f[i][x]=f[i-][f[i-][x]];
ys[x]=++z;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=f[][x])
{
f[][y]=x;
dep[y]=dep[x]+;
d[y]=d[x]+a[k].d;
dfs(y);
}
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=;i>=;i--)
if(dep[x]-dep[y]>=Bin[i])x=f[i][x];
if(x==y)return x;
for(int i=;i>=;i--)
if(dep[x]>=Bin[i]&&f[i][x]!=f[i][y])x=f[i][x],y=f[i][y];
return f[][x];
}
LL getdis(int x,int y){return d[x]+d[y]-d[LCA(x,y)]*;} struct myset
{
int t,id;
myset(){}
myset(int T,int ID){t=T;id=ID;}
friend bool operator>(myset s1,myset s2){return s1.t>s2.t;}
friend bool operator<(myset s1,myset s2){return s1.t<s2.t;}
}L,R,mL,mR;
set<myset>s;
int main()
{
Bin[]=;for(int i=;i<=;i++)Bin[i]=Bin[i-]*;
int n,x,y;LL dd;
scanf("%d",&n);
len=;memset(last,,sizeof(last));
for(int i=;i<n;i++)
{
scanf("%d%d%lld",&x,&y,&dd);
ins(x,y,dd);ins(y,x,dd);
}
f[][]=;z=;d[]=;
dfs(); int Q;LL ans=; char ss[];
scanf("%d",&Q);
while(Q--)
{
scanf("%s",ss+);
if(ss[]=='?')printf("%lld\n",ans/);
else if(ss[]=='+')
{
scanf("%d",&x);
if(!s.empty())
{
mL=*s.begin();mR=*--s.end(); if(ys[x]<mL.t)L=mR;
else L=*--s.lower_bound(myset(ys[x],x)); if(mR.t<ys[x])R=mL;
else R=*s.lower_bound(myset(ys[x],x)); ans-=getdis(L.id,R.id);
ans+=getdis(L.id,x);
ans+=getdis(x,R.id);
}
s.insert(myset(ys[x],x));
}
else
{
scanf("%d",&x);
s.erase(myset(ys[x],x));
if(!s.empty())
{
mL=*s.begin();mR=*--s.end(); if(ys[x]<mL.t)L=mR;
else L=*--s.lower_bound(myset(ys[x],x)); if(mR.t<ys[x])R=mL;
else R=*s.lower_bound(myset(ys[x],x)); ans+=getdis(L.id,R.id);
ans-=getdis(L.id,x);
ans-=getdis(x,R.id);
}
}
}
return ;
}

异象石

bzoj1977 同样是环的思想。先求出一棵最小生成树,对于非树边,必然是从和树边构成的环中,断掉最大的。然而题目要求严格次小生成树,当前非树边边权可能和最大边权相等,因此需要多维护一个严格次大值。这个和倍增求最近公共祖先的原理是一样的。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL; struct node
{
int x,y,next;LL d;
}a[];int len,last[];
void ins(int x,int y,LL d)
{
len++;
a[len].x=x;a[len].y=y;a[len].d=d;
a[len].next=last[x];last[x]=len;
}
int Bin[];
int f[][],dep[];
LL mx[][],sx[][];
LL smx(LL m1,LL s1,LL m2,LL s2)
{
LL ret=max(s1,s2);
if(m1!=m2)ret=max(ret,min(m1,m2));
return ret;
}
void dfs(int x)
{
for(int i=;Bin[i]<dep[x];i++)
{
f[i][x]=f[i-][f[i-][x]]; int m1=mx[i-][x],m2=mx[i-][f[i-][x]];
int s1=sx[i-][x],s2=sx[i-][f[i-][x]]; mx[i][x]=max(m1,m2);
sx[i][x]=smx(m1,s1,m2,s2);
} for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(f[][x]!=y)
{
f[][y]=x;
mx[][y]=a[k].d;
dep[y]=dep[x]+;
dfs(y);
}
}
}
void getmax(int x,int y,LL &mmax,LL &smax)
{
if(dep[y]>dep[x])swap(x,y); for(int i=;i>=;i--)
if(dep[x]-dep[y]>=Bin[i])
{
smax=smx(mmax,smax,mx[i][x],sx[i][x]);
mmax=max(mmax,mx[i][x]);
x=f[i][x];
} for(int i=;i>=;i--)
if(dep[x]>Bin[i]&&f[i][x]!=f[i][y])
{
smax=smx(mmax,smax,mx[i][x],sx[i][x]);
smax=smx(mmax,smax,mx[i][y],sx[i][y]);
mmax=max(mmax,max(mx[i][x],mx[i][y]));
x=f[i][x],y=f[i][y];
}
smax=smx(mmax,smax,mx[][x],sx[][x]);
smax=smx(mmax,smax,mx[][y],sx[][y]);
mmax=max(mmax,max(mx[][x],mx[][y]));
} struct edge{int x,y;LL d;}e[],lz[];
bool cmp(edge e1,edge e2){return e1.d<e2.d;}
int fa[];
int findfa(int x)
{
if(x==fa[x])return x;
fa[x]=findfa(fa[x]);return fa[x];
} int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)
scanf("%d%d%lld",&e[i].x,&e[i].y,&e[i].d);
sort(e+,e+m+,cmp); for(int i=;i<=n;i++)fa[i]=i;
int tp=;LL mind=;
for(int i=;i<=m;i++)
{
int fx=findfa(e[i].x),fy=findfa(e[i].y);
if(fx!=fy)
{
fa[fx]=fy;mind+=e[i].d;
ins(e[i].x,e[i].y,e[i].d);
ins(e[i].y,e[i].x,e[i].d);
}
else lz[++tp]=e[i];
}
m=tp; f[][]=;
memset(sx,-,sizeof(sx));
Bin[]=;for(int i=;i<=;i++)Bin[i]=Bin[i-]*;
dep[]=;dfs(); LL ans=(1LL<<);
for(int i=;i<=m;i++)
{
LL mmax=-(1LL<<),smax=-(1LL<<); getmax(lz[i].x,lz[i].y,mmax,smax);
if(mmax==lz[i].d)
{
if(smax!=-)
ans=min(ans,mind-smax+lz[i].d);
}
else ans=min(ans,mind-mmax+lz[i].d);
}
printf("%lld\n",ans);
return ;
}

bzoj1977

NOIP2009 D2T3

容易想到二分答案。对于军队而言,必然是越向上控制的叶子节点越多,那么尽量向上。先不管根,假如军队无法到达根的子节点,那么根据前面的贪心位置确定。否则,有两种情况:1、留在当前子节点,2、去往根的另一个孩子。

此时,题目就转化成:给你n个点,m个军队,军队有处在的点,有权值,点亦有权。军队可以占领自己的位置,或者占领点权+自己处于的点的点权<=自身权值的位置。

瓶颈就在于我可以留在当前的点,没有花费,特殊性造成的影响就是可能一个军队可能从一个点出发,却又回到这个点,相当于点权耗费两次,判断是否能够到达自己会出错。这样一来就无法把军队集中,统一贪心地分配。

如何去掉这个限制?对于一个军队,假如它的权值<=所处点权*2,必然不会去其他点。因为去,也只能去比当前点权值小的点,然而你还需要另一支军队占领当前点,既然这支军队可以占领当前点,那么也一定可以占领前一支军队占领的点。

处理完这个以后,我们就能够保证,即使点权耗费两次,而当前点的军队的权都是>二倍点权的,就不会出现不能到达自己的情况。由此就可以忽略第1个限制了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL; struct node
{
int x,y,next;LL d;
}a[];int len,last[];
void ins(int x,int y,LL d)
{
len++;
a[len].x=x;a[len].y=y;a[len].d=d;
a[len].next=last[x];last[x]=len;
}
int Bin[];LL d[][];
int f[][],dep[],son[];
void dfs(int x)
{
for(int i=;Bin[i]<dep[x];i++)
{
d[i][x]=d[i-][x]+d[i-][f[i-][x]];
f[i][x]=f[i-][f[i-][x]];
} son[x]=;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=f[][x])
{
son[x]++;
dep[y]=dep[x]+;
f[][y]=x;
d[][y]=a[k].d;
dfs(y);
}
}
} int m,p[],s[];bool v[],b[];
struct stack{LL d;int id;}sta[];int top;
int arlen,ctlen;LL ar[],ct[];
bool check(LL mid)
{
memset(v,false,sizeof(v));
for(int k=last[];k;k=a[k].next)b[a[k].y]=false;
memcpy(s,son,sizeof(s));
top=;
for(int i=;i<=m;i++)
{
int x=p[i];LL w=mid;
for(int j=;j>=;j--)
if(dep[x]>Bin[j]&&w>=d[j][x]&&f[j][x]!=)w-=d[j][x],x=f[j][x]; if(f[][x]!=)
{
bool bk=false;
for(int k=x;k!=;k=f[][k])if(v[k]==true){bk=true;break;}
if(bk==false)
{
v[x]=true;
s[f[][x]]--;
while(s[f[][x]]==)
{
v[f[][x]]=true;
x=f[][x],s[f[][x]]--;
if(f[][x]==){b[x]=true;break;}
}
}
}
else sta[++top].d=w,sta[top].id=x;
} arlen=;
for(int i=;i<=top;i++)
if(b[sta[i].id]==false)
{
if(d[][sta[i].id]*>=sta[i].d)b[sta[i].id]=true;
else ar[++arlen]=sta[i].d-d[][sta[i].id];
}
else ar[++arlen]=sta[i].d-d[][sta[i].id];
sort(ar+,ar+arlen+); ctlen=;
for(int k=last[];k;k=a[k].next)
if(b[a[k].y]==false)ct[++ctlen]=a[k].d;
sort(ct+,ct+ctlen+); int j=;
for(int i=;i<=ctlen;i++)
{
while(j<arlen&&ar[j]<ct[i])j++;
if(ar[j]<ct[i])return false;
j++;
if(j>arlen&&i<ctlen)return false;
}
return true;
}
int main()
{
int n,x,y;LL dd;
scanf("%d",&n);
for(int i=;i<n;i++)
{
scanf("%d%d%lld",&x,&y,&dd);
ins(x,y,dd);ins(y,x,dd);
}
Bin[]=;for(int i=;i<=;i++)Bin[i]=Bin[i-]*;
f[][]=;dep[]=;dfs(); scanf("%d",&m); if(son[x]>m){printf("-1\n");return ;}
for(int i=;i<=m;i++)scanf("%d",&p[i]); LL l=,r=*1e13,ans;
while(l<=r)
{
LL mid=(l+r)/;
if(check(mid))
{
ans=mid;
r=mid-;
}
else l=mid+;
}
printf("%lld\n",ans);
return ;
}

疫情控制

0x63树的直径与最近公共祖先的更多相关文章

  1. 51 nod 1681 公共祖先 (主席树+dfs序)

    1681 公共祖先 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题   有一个庞大的家族,共n人.已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边). 在另 ...

  2. 【并查集】【树】最近公共祖先LCA-Tarjan算法

    最近公共祖先LCA 双链BT 如果每个结点都有一个指针指向它的父结点,于是我们可以从任何一个结点出发,得到一个到达树根结点的单向链表.因此这个问题转换为两个单向链表的第一个公共结点(先分别遍历两个链表 ...

  3. 洛谷P3379 【模板】最近公共祖先(LCA)(树链剖分)

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  4. 51nod 1681 公共祖先 | 树状数组

    51nod 1681 公共祖先 有一个庞大的家族,共n人.已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边). 在另一个未知的平行宇宙,这n人的祖辈关系仍然是树形结构,但他们相互之间的关系却完 ...

  5. CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先)

    CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先) 题意分析 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天, ...

  6. 树论讲解——最近公共祖先(lca)

    最近公共祖先?! 有人肯定要问:什么是最近公共祖先???!! 好那我们现在就来说说什么是最近公共祖先吧! 最近公共祖先有一个好听的名字叫——lca 这是一种算法,这个算法基于并查集和深度优先搜索.算法 ...

  7. hiho #1062 : 最近公共祖先·一(树,最近祖先)

    #1062 : 最近公共祖先·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho最近发现了一个神奇的网站!虽然还不够像58同城那样神奇,但这个网站仍然让小Ho乐在 ...

  8. [51nod 1681]公共祖先(dfs序+线段树合并)

    [51nod 1681]公共祖先(dfs序+线段树合并) 题面 给出两棵n(n<=100000)个点的树,对于所有点对求它们在两棵树中公共的公共祖先数量之和. 如图,对于点对(2,4),它们在第 ...

  9. 线段树、最短路径、最小生成树、并查集、二分图匹配、最近公共祖先--C++模板

    线段树(区间修改,区间和): #include <cstdio> #include <iostream> #include <cstring> using name ...

随机推荐

  1. MemcachedClient 使用说明

    上一篇介绍了Memcached基本使用方法<Memcached使用手册>,下面介绍java如何操作memcached.使用的是java_memcached-release_2.6.6. 一 ...

  2. 【Oracle】DBMS_STATS.GATHER_TABLE_STATS

    月初一直在忙保监会报送的事情,苦逼的保险行业的ETL大家都懂的.今天闲来无事查看了一下前阵子的报送存储过程,发现系统隔一段时间就会调用一次DBMS_STATS.GATHER_TABLE_STATS,所 ...

  3. Redmine使用指南

    公司之前使用JIRA登bug,但是客户在美国,他们习惯于用Redmine登bug,所以我们也开始在Redmine登bug,找来一个比较全面的Redmine使用指南,不懂时直接查看. http://bl ...

  4. Centos6.6 编译安装nginx

    一.基本环境 nginx 1.9版以后增加了一些新的特性,支持tcp负载均衡,不过这次还是用1.8.0,这里面有个memcached的代理模块,有时间再测试下 1.centos6.6 2.nginx1 ...

  5. (转)Bootstrap 之 Metronic 模板的学习之路 - (4)源码分析之脚本部分

    https://segmentfault.com/a/1190000006709967 上篇我们将 body 标签主体部分进行了简单总览,下面看看最后的脚本部门. 页面结尾部分(Javascripts ...

  6. Dispatch Queues and Thread Safety

    Dispatch Queues and Thread Safety It might seem odd to talk about thread safety in the context of di ...

  7. 初识 Django

    HTTP协议 HTTP(hypertext transport protocol),即超文本传输协议.这个协议详细规定了浏览器和万维网服务器之间互相通信的规则. HTTP就是一个通信规则,通信规则规定 ...

  8. 接口测试与Postman

    阅读目录 1.接口测试简介 1.1 什么是接口测试  1.2 接口测试的必要性 1.3 接口测试流程 1.4 接口文档 1.5 接口测试用例设计 1.6 接口测试用例模板 2.Postman 2.1 ...

  9. ZooKeeper 运维经验

    转自:http://www.juvenxu.com/2015/03/20/experiences-on-zookeeper-ops/ ZooKeeper 运维经验 ZooKeeper 是分布式环境下非 ...

  10. Java基础之 HelloWorld

    1. Java发展史 参考: https://www.cnblogs.com/guoqingyan/p/5667064.html 2. Java中 JDK, JRE, JVM之间的关系 参考: htt ...