2016-2017 ACM-ICPC, NEERC, Northern Subregional Contest G(gym/101142 G)
题意:一棵树,给出每个点的父亲节点,叶子节点是house,1是根节点,也是水源,现在有2个操作,+ x表示劫匪入侵第x个house, - x表示劫匪退出第x个house,每次操作后求最少切断几个边,可以把劫匪入侵的house的水源切断,并且求出收到影响的普通house的最小个数
思路:LCA, 根节点下面每一个节点的子树上面,如果有2个或以上的house被入侵,那么最多只需要切断一条边即可,切断的边是这个子树下面所有被入侵的house的LCA与LCA的父亲连接的边,
关于如何快速求 p 个点的最近公共祖先(LCA),只要对该子树被歹徒占领的点中 dfs 序最小的点和最大的点求 LCA 。
对根节点下每一个子树动态维护一个set即可,set用于维护这个子树下面dfs序最大和最小的节点,代码比较不好写
AC代码:
#include "iostream" #include "iomanip" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector" #include "set" #include "map" #include "algorithm" #include "stdio.h" #include "math.h" #pragma comment(linker, "/STACK:102400000,102400000") #define bug(x) cout<<x<<" "<<"UUUUU"<<endl; #define mem(a,x) memset(a,x,sizeof(a)) #define step(x) fixed<< setprecision(x)<< #define mp(x,y) make_pair(x,y) #define pb(x) push_back(x) #define ll long long #define endl ("\n") #define ft first #define sd second #define lrt (rt<<1) #define rrt (rt<<1|1) using namespace std; const ll mod=1e9+7; const ll INF = 1e18+1LL; const int inf = 1e9+1e8; const double PI=acos(-1.0); const double eps=1e-9; const int N=1e5+100; set<pair<int,int> > se[N]; vector<int> G[N]; pair<int,int> in[N]; int cnt,top[N], son[N], siz[N], dep[N], vis[N], hos[N], ins[N], ans[N], fa[N]; void dfs0(int u, int t){ siz[u]=1; ins[u]=t; dep[u]=dep[fa[u]]+1; if(vis[u]==0) hos[u]=1; for(auto v : G[u]){ if(u==1) t=v; dfs0(v, t); siz[u]+=siz[v]; hos[u]+=hos[v]; if(siz[son[u]] < siz[v]) son[u]=v; } } void dfs1(int u, int tp){ in[u].ft=cnt++; in[u].sd=u; top[u]=tp; if(son[u]!=0) dfs1(son[u], tp); for(auto v : G[u]){ if(v!=son[u]) dfs1(v,v); } } int LCA(int u, int v){ while(top[u] != top[v]){ if(dep[top[u]] > dep[top[v]]) swap(u,v); v=fa[top[v]]; } if(dep[u] > dep[v]) swap(u, v); return u; } int main(){ freopen("gangsters.in","r",stdin); freopen("gangsters.out","w",stdout); int n,m,u,out=0, out1=0; char s[10]; scanf("%d %d",&n,&m); for(int i=2; i<=n; ++i){ scanf("%d",&u); G[u].pb(i); fa[i]=u; vis[u]=1; } dfs0(1,1); dfs1(1,1); for(int i=1; i<=m; ++i){ scanf("%s %d", s, &u); if(s[0]=='+'){ se[ins[u]].insert(in[u]); if(se[ins[u]].size()==1){ ans[ins[u]]=0, out1++; } else{ int lca=LCA(se[ins[u]].begin()->sd, se[ins[u]].rbegin()->sd); out-=ans[ins[u]]; ans[ins[u]]=hos[lca]-se[ins[u]].size(); out+=ans[ins[u]]; } } else{ se[ins[u]].erase(in[u]); if(se[ins[u]].size()<2){ out-=ans[ins[u]]; ans[ins[u]]=0; if(se[ins[u]].size()==0) out1--; } else{ int lca=LCA(se[ins[u]].begin()->sd, se[ins[u]].rbegin()->sd); out-=ans[ins[u]]; ans[ins[u]]=hos[lca]-se[ins[u]].size(); out+=ans[ins[u]]; } } printf("%d %d\n",out1, out); } return 0; }