您的位置:首页 > 其它

hdu5296(倍增lca)

2015-08-27 15:27 381 查看
对于这道题,虽然题中有个求最小值,但它不是需要进行选择边的那种题,如果给定确定的点,把这些点连到一起,使得任意两点可达,那么只有一种方法(当然,去除掉多余的边,即叶子节点和根节点都必须在集合中)。所以可以每次加边时,只要找到一种方法,把它加入到已有树中就可以了,并且只有一种方法(当然,还是要去掉多余的边)。

至于根据dfs序找,假设要加入x这个点:

1)集合中有dfs序大于x的,并且也有小于x的,假设dfs序大于x的那个点是x的子节点,根据公式,可以的出,花费为0,余现实情况相符。

2)同1)中找出的两个点,只不过dfs序大于x的那个点不是x的子节点,那么此时必然离x最近的某个祖先节点虽然没在集合中,但是也己经在那个前几步生成的树当中,此时,只要把x到这个祖父节点的距离找出来,就是最小花费。根据公式所求,符合现实情况。

3)如果集合中不同时存在dfs序大于x的,和dfs序小于x的,假设,选出的此时集合中dfs序最大和最小的点,都是x的子节点,根据公式所求,符合现实要求。

4)选点同3,只是选出的两个点都是dfs序小于x的,那么如果此时这两个点的最近公共祖先是x的祖先节点,按照公式所求,符合现实要求,如果不是,按照公式所求,符合现实要求。

5)选点同3,只是选出的两个点都是dfs序大于x的,并且都不是x的子节点,同4

6)选点同3,只是选出的两个点都是dfs序大于x的,一个是x的子节点,一个不是x的子节点,按照公式所求,符合现实要求。

至于那道题,怎么正着推出这道题的解,现在只能把它当做一个结论来记了。

假设节点要连接到一个链中,链的定点(x,y),那么u连接到x的距离是dfn[u] + dfn[x] - 2dfn[ lca(u,x) ] ;

u连接到y的距离dfn[u] + dfn[y] - 2dfn[ lca(u,x) ] :

x连接到y的距离dfn[x] + dfn[y] - 2dfn[ lca(x,y) ] :

u连接到x-y这个链的距离 = (u到y+u到x-x到y)/2

迭代器讲解链接:http://www.cnblogs.com/yc_sunniwell/archive/2010/06/25/1764934.html

代码参考链接:http://www.bubuko.com/infodetail-988787.html

set的链接:http://baike.baidu.com/link?url=Uxof9XTaQURDJ9H8vSaVz4Cs8SOqee2kHyj37CBr2GrLzj-IqYGI5PGyBonm5C_oRWeBBbBbzfGDdlByuw1owK

用的是倍增lca。

(听说今天学校其余食堂就开门了,一共7个食堂,假期就开了4个,但是关的那三个中偏偏又两个常去的,开的这4个中虽然有两个个平常会去的,但是其中有一个,经常吃的窗口关了,所以这个暑假的食堂好凄惨!



2015.8.31:

这道题也是刚做不久,回忆了一下过程,还沥沥在目!


#include<stdio.h>
#include<string.h>
#include<iostream>
#include<set>
using namespace std;
#define N 100010
#define M 20

int deep
,dis
;
int vis
;
int head
,to[2*N],nextedge[2*N],wedge[2*N];
int dfsxv
,dfsxf
;
int fadp
[M];
set<int> s;
set<int>::iterator iter;
int cou;
int time;

void add(int a,int b,int c){
to[cou]=b;nextedge[cou]=head[a];wedge[cou]=c;head[a]=cou++;
}

void dfs(int u,int fa){
dfsxv[u]=++time;
dfsxf[time]=u;

for(int i=head[u];i!=-1;i=nextedge[i]){
int v=to[i];

if(v==fa){
continue;
}
else{
deep[v]=deep[u]+1;
dis[v]=dis[u]+wedge[i];
fadp[v][0]=u;
for(int j=1;(1<<j)<deep[v];j++){
fadp[v][j]=fadp[fadp[v][j-1]][j-1];
}
dfs(v,u);
}
}

return;
}

int lca(int a,int b){
if(deep[a]<deep[b]){
swap(a,b);
}

int tempi;
for(tempi=0;(1<<tempi)<deep[a];tempi++);
tempi--;
for(int i=tempi;i>=0;i--){//使得合理答案永远在(a,a+2*tempi-1)的范围内
if(deep[fadp[a][i]]>=deep[b]){
a=fadp[a][i];
}
}

if(a==b){
return a;
}
for(tempi=0;(1<<tempi)<deep[a];tempi++);
tempi--;
for(int i=tempi;i>=0;i--){//是的合理答案,一直在(a+1,a+2*tempi)的范围内
if(fadp[a][i]!=fadp[b][i]){
a=fadp[a][i];
b=fadp[b][i];
}
}

return fadp[a][0];//尝试:在上面的循环中去掉i=0.->不能去掉i=0
}

int solve(int u){
if(s.size()==0){
return 0;
}
else{
int x,y;
iter=s.lower_bound(dfsxv[u]);
if(iter==s.end()||iter==s.begin()){
x=dfsxf[*s.begin()];//注意*
y=dfsxf[*s.rbegin()];
}
else{
x=dfsxf[*iter];
iter--;
y=dfsxf[*iter];
}
return dis[u]+dis[lca(x,y)]-dis[lca(u,x)]-dis[lca(u,y)];
}
}

int main(){
int t;
int n,q;
int a,b,c;

scanf("%d",&t);
for(int cas=1;cas<=t;cas++){
scanf("%d%d",&n,&q);

memset(head,-1,sizeof(head));
cou=0;
for(int i=1;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}

memset(fadp,0,sizeof(fadp));//要是0,而不是1
deep[0]=-1;
deep[1]=1;
dis[1]=0;
time=0;
dfs(1,-1);

memset(vis,0,sizeof(vis));
s.clear();//如果用容器什么的,也要考虑清理
int ans=0;
printf("Case #%d:\n",cas);
for(int i=0;i<q;i++){
scanf("%d%d",&a,&b);
if(a==1){
if(!vis[b]){
vis[b]=1;
ans+=solve(b);
s.insert(dfsxv[b]);
}
}
else if(a==2){
if(vis[b]){
vis[b]=0;
s.erase(dfsxv[b]);
ans-=solve(b);
}
}

printf("%d\n",ans);
}
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: