0x63树的直径与最近公共祖先
凉
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树的直径与最近公共祖先的更多相关文章
- 51 nod 1681 公共祖先 (主席树+dfs序)
1681 公共祖先 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 有一个庞大的家族,共n人.已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边). 在另 ...
- 【并查集】【树】最近公共祖先LCA-Tarjan算法
最近公共祖先LCA 双链BT 如果每个结点都有一个指针指向它的父结点,于是我们可以从任何一个结点出发,得到一个到达树根结点的单向链表.因此这个问题转换为两个单向链表的第一个公共结点(先分别遍历两个链表 ...
- 洛谷P3379 【模板】最近公共祖先(LCA)(树链剖分)
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...
- 51nod 1681 公共祖先 | 树状数组
51nod 1681 公共祖先 有一个庞大的家族,共n人.已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边). 在另一个未知的平行宇宙,这n人的祖辈关系仍然是树形结构,但他们相互之间的关系却完 ...
- CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先)
CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先) 题意分析 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天, ...
- 树论讲解——最近公共祖先(lca)
最近公共祖先?! 有人肯定要问:什么是最近公共祖先???!! 好那我们现在就来说说什么是最近公共祖先吧! 最近公共祖先有一个好听的名字叫——lca 这是一种算法,这个算法基于并查集和深度优先搜索.算法 ...
- hiho #1062 : 最近公共祖先·一(树,最近祖先)
#1062 : 最近公共祖先·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho最近发现了一个神奇的网站!虽然还不够像58同城那样神奇,但这个网站仍然让小Ho乐在 ...
- [51nod 1681]公共祖先(dfs序+线段树合并)
[51nod 1681]公共祖先(dfs序+线段树合并) 题面 给出两棵n(n<=100000)个点的树,对于所有点对求它们在两棵树中公共的公共祖先数量之和. 如图,对于点对(2,4),它们在第 ...
- 线段树、最短路径、最小生成树、并查集、二分图匹配、最近公共祖先--C++模板
线段树(区间修改,区间和): #include <cstdio> #include <iostream> #include <cstring> using name ...
随机推荐
- elasticsearch5.3.0 bulk index 性能调优实践
elasticsearch5.3.0 bulk index 性能调优实践 通俗易懂
- SQLCE本地数据库
SQLCE是一个标准得关系数据库,可以使用 LINQ 和DateContext来处理本地数据库数据库. 使用SQLCE 要在代码中使用本地数据库功能,需要添加以下命名空间 : using System ...
- Android开机图片替换
Android开机图片替换 Android从启动到进入Launcher一共会展示三张图片,如果只是更换静态图则更换这三张图片即可,要想换成动画那就要另外操作. 首先查找这个文件: /bootab ...
- Unity引擎的Player Settings介绍
我用的是unity5.4.3版本的 一.窗口打开: 从菜单栏查看播放器设置,选择 Edit->Project Settings->Player 二.全局设置 第一部分: Company N ...
- ★Java语法(二)——————————数据类型及装换
整数类型: 1.byte型:8位(1字节) 范围:-128~127 用法:byte x = 35 : 2.short型:16位(2字节) 范围:-32768~32767 用法:short x = ...
- js 响应事件
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- [Jxoi2012]奇怪的道路 题解(From luoguBlog)
题面 状压好题 1<= n <= 30, 0 <= m <= 30, 1 <= K <= 8 这美妙的范围非状压莫属 理所当然地,0和1代表度的奇偶 dp[i][j ...
- Java设计模式之JDK动态代理原理
动态代理核心源码实现public Object getProxy() { //jdk 动态代理的使用方式 return Proxy.newProxyInstance( this.getClass(). ...
- 基于MATLAB的语音信号处理
一.图形界面设计 1.新建GUI界面 2.新建空白页 3.命名为"yydsp",打开界面 4.拖放控件 5.按预定功能修改界面 6.填写Callback函数 未填写前的代码: fu ...
- JavaScript、Dom和jQuery
var obj=document.getElementById('t1') obj.innerText obj.innerHTML 1.javascript 插入代码如下: <script ty ...