您的位置:首页 > 其它

HDU 5296 Annoying problem (LCA,变形)

2015-07-22 14:24 183 查看
题意:

  给一棵n个节点的树,再给q个操作,初始集合S为空,每个操作要在一个集合S中删除或增加某些点,输出每次操作后:要使得集合中任意两点互可达所耗最小需要多少权值。(记住只能利用原来给的树边。给的树边已经有向。10万个点,10万个操作)

思路:只能用 O(nlogn)的复杂度。官方题解:

  

#include <bits/stdc++.h>
#define LL long long
#define pii pair<int,int>
#define INF 0x7f7f7f7f
using namespace std;
const int N=100100;
vector<int> chld
;    //孩子
unordered_map<int,int> weight
;
int dis
;
int mapp
;
int seq
;          //seq记录DFS序
int anti_seq
;    //anti记录第几个访问的是谁
bool inset
;
int ans, num;
set<int> sett;

void DFS(int x)
{
seq[x]=++num;
for(int i=0; i<chld[x].size(); i++)
{
int t=chld[x][i];
if(!seq[t])    DFS(t);
}
}

unordered_map<int,int> vis;
int LCA(int a,int b)
{
vis.clear();
while(mapp[a])
{
vis[a]=1;
a=mapp[a];
}
vis[a]=1;
while( !vis[b] )    b=mapp[b];
return b;
}

void cal_weight(int n)
{
for(int i=1; i<=n; i++)
{
int sum=0, s=i;
while(mapp[s])
{
sum+=weight[mapp[s]][s];
s=mapp[s];
}
dis[i]=sum;
}
}

int ins(int u)
{
sett.insert(seq[u]);
if(sett.size()==1)  {ans=0;return 0;}

set<int>::iterator it=sett.find(seq[u]);

int x=0, y=0;
if(*it==*sett.begin())    //已经是最小
{
x=anti_seq[*(++it)];
y=anti_seq[*(sett.rbegin())];
}
else if(*it==*sett.rbegin()) //已经是最大
{
x=anti_seq[*(--it)];
y=anti_seq[*(sett.begin())];
}
else
{
x=anti_seq[ *(--it)];
y=anti_seq[ *(++(++it))  ];
}

ans+=dis[u]-dis[LCA(u,x )]- dis[LCA(u,y )] + dis[LCA(x,y)];
return ans;
}

int del(int u)
{
if(sett.size()==2)
{
sett.erase(seq[u]);
ans=0;
return 0;
}

set<int>::iterator it=sett.find(seq[u]);
int x=0, y=0;
if(*it==*sett.begin())    //已经是最小
{
x=anti_seq[*(++it)];
y=anti_seq[*(sett.rbegin())];
}
else if(*it==*sett.rbegin()) //已经是最大
{
x=anti_seq[*(--it)];
y=anti_seq[*(sett.begin())];
}
else
{
x=anti_seq[ *(--it)];
y=anti_seq[ *(++(++it))  ];
}
ans-=dis[u]-dis[LCA(u,x )]- dis[LCA(u,y )] + dis[LCA(x,y)];
sett.erase(seq[u]);
return ans;
}

int main()
{
freopen("input.txt", "r", stdin);
int a, b, c, t, q, n, j=0;
cin>>t;
while(t--)
{
scanf("%d%d", &n, &q);
for(int i=0; i<=n+10; i++)    chld[i].clear(),weight[i].clear();

memset(inset, 0, sizeof(inset));
memset(seq, 0, sizeof(seq));
memset(anti_seq, 0, sizeof(anti_seq));
memset(mapp, 0, sizeof(mapp));
memset(dis,0,sizeof(dis));
sett.clear();
ans=0;
num=0;

for(int i=1; i<n; i++)
{
scanf("%d%d%d",&a,&b,&c);
chld[a].push_back(b);
weight[a][b]=c;
mapp[b]=a;
}
printf("Case #%d:\n",++j);
int root=n;        //获得树根
while(mapp[root])   root=mapp[root];
DFS(root);          //获得DFS序

for(int i=1; i<=n; i++)      anti_seq[seq[i]]=i;      //反向索引
cal_weight(n);        //计算每个点到根的权
while(q--)
{
scanf("%d%d",&a,&b);
if(a==1)    //如果不存在于集合中,则插,有则不插
{
if(inset[b])    printf("%d\n", ans);  //已经存在,不必计算
else
{
inset[b]=1;
printf("%d\n", ins(b));
}
}
else    //如果存在于集合中,则删,否则不删。
{
if(inset[b])    //存在,要删
{
inset[b]=0;
printf("%d\n",del(b));
}
else    printf("%d\n",ans);
}
}
}
return 0;
}


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