D Alliances 二分 lca 欧拉序 换根
链接:https://ac.nowcoder.com/acm/contest/27836/D
来源:牛客网
题目描述
树国是一个有n个城市的国家,城市编号为1∼n。连接这些城市的道路网络形如一棵树,
即任意两个城市之间有恰好一条路径。城市中有k个帮派,编号为1∼k。每个帮派会占据一些城市,以进行非法交易。有时帮派之间会结盟,这就使得城市更加不安全了。同一座城市中可能有多个帮派。
当一些帮派结成联盟时,他们会更加强大,同时也更加危险。他们所控制的城市数会显著增加。具体地,一个联盟控制的城市是联盟中所有帮派所占据的城市,再加上这些城市两两之间路径上的所有城市。
shy是树国的市长,他想要选择一个城市作为首都。在决定之前,他要先做一些调研。为此,他找来你帮他回答一些询问,你能做到吗?在每个询问中,shy会选择一个城市作为首都,同时会告诉你当前活跃的帮派的集合。在这个询问中,你只需要考虑给定的集合中的帮派,其他的帮派你可以当作不存在。已知给定集合中的这些帮派结成了联盟,shy希望抓获联盟中的人,以得到关于整个联盟的一些信息。为此,他要找到被联盟控制的所有城市中离首都最近的一座城市到首都的距离。有可能首都本身就被控制了,此时答案为0。请注意,询问之间相互独立,互不影响。
输入描述:
输入的第一行包含一个整数n,代表树国中的城市数。
接下来n−1行,每行包含两个整数u和v,代表城市u和v之间存在一条道路。
接下来一行包含一个整数k,代表树国中的帮派数。
接下来k行,每行描述一个帮派。第i行的第一个整数c[i]代表第i个帮派占据的城市数,接下来c[i]个整数,代表被第i个帮派占据的城市。
接下来一行包含一个整数Q,代表询问数。
接下来Q行,每行描述一个询问。每行的前两个整数V和t[i]代表本次询问中的首都与需要考虑的帮派集合的大小。接下来t[i]个整数代表本次询问中需要考虑的帮派。.
输出描述:
对于每个询问,输出一行,包含一个整数,代表询问的答案。
备注:
对于30%的数据,1≤n,k,Q≤1000, 1≤每个帮派占据城市数之和≤1000, 1≤每个询问中考虑的帮派数之和≤1000 对于60%的数据,1≤n,k,Q≤100000, 1≤每个帮派占据城市数之和≤100000, 1≤每个询问中考虑的帮派数之和≤100000 对于100%的数据,1≤n,k,Q≤500000, 1≤每个帮派占据城市数之和≤500000, 1≤每个询问中考虑的帮派数之和≤500000
分析
就是以不同的节点算作根V,计算多个点的lca :p到根节点的距离。
三种情况
1.lca(p,V) != V,直接算 dis(lca(p,V),V);
2.lca(p,V) == V,同时V 在所有帮派的链上,那就输出0
3.lca(p,V) == V,但是V 不在所有帮派的链上,有两种情况,V在lca 的左边,V在lca的右边,不管是哪种情况,只要找到在这条链上离V最近的点就可以了。
考虑欧拉序,最近的点,要么在根节点左边的链上,要么在根节点右边的链上
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int MAX_N=1e6+20; const int DEG=20; const int INF=0x3f3f3f3f; const LL MOD=1e9+7; int T; int N; //链式前向星村边 int head[MAX_N],tot; struct Edge{ int to,nxt; }edge[MAX_N*2]; void addedge(int u,int v){ edge[tot].to=v; edge[tot].nxt=head[u]; head[u]=tot++; } void init(){ tot=0; memset(head,-1,sizeof(head)); } //求LCA int fa[MAX_N][DEG]; int deg[MAX_N]; void BFS(int root){ queue<int>que; deg[root]=0; fa[root][0]=root; que.push(root); while(!que.empty()){ int tmp=que.front(); que.pop(); for(int i=1;i<DEG;i++){ fa[tmp][i]=fa[fa[tmp][i-1]][i-1]; } for(int i=head[tmp];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(v==fa[tmp][0])continue; deg[v]=deg[tmp]+1; fa[v][0]=tmp; que.push(v); } } } int LCA(int u,int v){ if(deg[u]>deg[v])swap(u,v); int hu=deg[u],hv=deg[v]; int tu=u,tv=v; for(int det=hv-hu,i=0;det;det>>=1,i++){ if(det&1)tv=fa[tv][i]; } if(tu==tv)return tu; for(int i=DEG-1;i>=0;i--){ if(fa[tu][i]==fa[tv][i])continue; tu=fa[tu][i]; tv=fa[tv][i]; } return fa[tu][0]; } //求DFS序 int dfsn[MAX_N]; int pos[MAX_N]; int dfst=0; void DFS(int v,int fa){ dfsn[v]=++dfst; pos[dfst]=v; for(int i=head[v];i!=-1;i=edge[i].nxt){ int u=edge[i].to; if(u==fa)continue; DFS(u,v); } } int dis(int u,int v){ int res=deg[u]+deg[v]-2*deg[LCA(u,v)]; return res; } //lc存每一个帮派的LCA //g存每一个帮派的DFS序 int lc[MAX_N]; vector<int> g[MAX_N]; int n; int u,v; void input(){ init(); scanf("%d",&n); for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } BFS(1); DFS(1,-1); int k; scanf("%d",&k); for(int i=1;i<=k;i++){ int c,x; scanf("%d",&c); for(int j=1;j<=c;j++){ scanf("%d",&x); if(j==1)lc[i]=x; else lc[i]=LCA(lc[i],x); g[i].push_back(dfsn[x]); } sort(g[i].begin(),g[i].end()); //cout<<lc[i]<<endl; } //cout<<"****"<<endl; } int t[MAX_N]; void solve(){ int q,u,cnt; scanf("%d",&q); while(q--){ scanf("%d%d",&u,&cnt); for(int i=1;i<=cnt;i++){ scanf("%d",&t[i]); } int lca=lc[t[1]]; for(int i=2;i<=cnt;i++)lca=LCA(lca,lc[t[i]]); if(LCA(lca,u)!=lca){ printf("%d\n",dis(lca,u)); continue; } int ans=INF; for(int i=1;i<=cnt;i++){ int tmp=t[i]; auto p=lower_bound(g[tmp].begin(),g[tmp].end(),dfsn[u]); if(p!=g[tmp].end()){ ans=min(ans,dis(u,LCA(u,pos[*p]))); } if(p!=g[tmp].begin()){ ans=min(ans,dis(u,LCA(u,pos[*prev(p)]))); } } printf("%d\n",ans); } } int main(){ //ios::sync_with_stdio(false); //cin.tie(0),cout.tie(0); //freopen("1.in","r",stdin); input(); solve(); }

浙公网安备 33010602011771号