Newcoder 4 C.Alliances(線上倍增LCA+二分)
Description
樹國是一個有 個城市的國家,城市編號為 ∼ 。連線這些城市的道路網路形如一棵樹,即任意兩個城市之間有恰好一條路徑。城市中有 個幫派,編號為 ∼ 。每個幫派會佔據一些城市,以進行非法交易。有時幫派之間會結盟,這就使得城市更加不安全了。同一座城市中可能有多個幫派。
當一些幫派結成聯盟時,他們會更加強大,同時也更加危險。他們所控制的城市數會顯著增加。具體地,一個聯盟控制的城市是聯盟中所有幫派所佔據的城市,再加上這些城市兩兩之間路徑上的所有城市。
是樹國的市長,他想要選擇一個城市作為首都。在決定之前,他要先做一些調研。為此,他找來你幫他回答一些詢問,你能做到嗎?在每個詢問中, 會選擇一個城市作為首都,同時會告訴你當前活躍的幫派的集合。在這個詢問中,你只需要考慮給定的集合中的幫派,其他的幫派你可以當作不存在。已知給定集合中的這些幫派結成了聯盟, 希望抓獲聯盟中的人,以得到關於整個聯盟的一些資訊。為此,他要找到被聯盟控制的所有城市中離首都最近的一座城市到首都的距離。有可能首都本身就被控制了,此時答案為 。請注意,詢問之間相互獨立,互不影響。
Input
輸入的第一行包含一個整數 ,代表樹國中的城市數。
接下來 行,每行包含兩個整數 和 ,代表城市 和 之間存在一條道路。
接下來一行包含一個整數 ,代表樹國中的幫派數。
接下來 行,每行描述一個幫派。第 行的第一個整數 代表第 個幫派佔據的城市數,接下來 個整數,代表被第 個幫派佔據的城市。
接下來一行包含一個整數 ,代表詢問數。
接下來 行,每行描述一個詢問。每行的前兩個整數 和 代表本次詢問中的首都與需要考慮的幫派集合的大小。接下來 個整數代表本次詢問中需要考慮的幫派。
Output
對於每個詢問,輸出一行,包含一個整數,代表詢問的答案。
Sample Input
7
1 2
1 3
2 4
2 5
3 6
3 7
2
2 6 7
1 4
3
5 1 2
1 1 1
5 2 1 2
Sample Output
2
1
1
Solution
一個幫派可以控制的所有城市為以這些城市的公共祖先為根節點的子樹去掉以這些城市中一部分為根的小子樹,對多個幫派同理,那麼對於每組查詢,求出這些幫派佔領城市的公共祖先 ,且對於每個幫派,把其控制的城市的 序升序排好,二分查詢首都 序處於的區間,那麼首都到以該幫派控制城市的最短距離會從首都到該區間兩端點的城市距離產生,故剩餘問題是如何求出首都到一個被控制城市的距離,假設首都為 ,該被控制城市為 ,兩點公共祖先為 ,若 在 為根的子樹中,那麼 必然在 控制區域,此時距離為 ,若 不在 為根的子樹中,那麼 為 的祖先,此時也沒必要從 再走到 ,直接走到 即可,此時距離為
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=500005;
int n,m,q,p[maxn][20],dep[maxn],dfn[maxn],Id[maxn],Index,top[maxn],a[maxn];
vector<int>g[maxn],vec[maxn];
vector<int>::iterator it;
void dfs(int u,int fa)
{
Id[Index]=u;
dfn[u]=Index++;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v==fa)continue;
dep[v]=dep[u]+1;
p[v][0]=u;
dfs(v,u);
}
}
void lca_init()
{
dep[1]=Index=1;
memset(p,-1,sizeof(p));
dfs(1,0);
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
if(~p[i][j-1])p[i][j]=p[p[i][j-1]][j-1];
}
int lca(int a,int b)
{
int i,j;
if(dep[a]<dep[b])swap(a,b);
for(i=0;(1<<i)<=dep[a];i++);
i--;
for(j=i;j>=0;j--)
if(dep[a]-(1<<j)>=dep[b])
a=p[a][j];
if(a==b)return a;
for(j=i;j>=0;j--)
if(p[a][j]!=-1&&p[a][j]!=p[b][j])
a=p[a][j],b=p[b][j];
return p[a][0];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v),g[v].push_back(u);
}
lca_init();
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int num,u;
scanf("%d",&num);
for(int j=1;j<=num;j++)
{
scanf("%d",&u);
vec[i].push_back(dfn[u]);
if(j==1)top[i]=u;
else top[i]=lca(top[i],u);
}
sort(vec[i].begin(),vec[i].end());
}
scanf("%d",&q);
while(q--)
{
int u,num,Top,ans=n;
scanf("%d%d",&u,&num);
for(int i=1;i<=num;i++)
{
scanf("%d",&a[i]);
if(i==1)Top=top[a[i]];
else Top=lca(Top,top[a[i]]);
}
for(int i=1;i<=num;i++)
{
it=lower_bound(vec[a[i]].begin(),vec[a[i]].end(),dfn[u]);
if(it!=vec[a[i]].end())
{
int v=lca(u,Id[*it]);
ans=min(ans,dep[u]-dep[v]+max(dep[Top]-dep[v],0));
}
if(it!=vec[a[i]].begin())
{
it--;
int v=lca(u,Id[*it]);
ans=min(ans,dep[u]-dep[v]+max(dep[Top]-dep[v],0));
}
}
printf("%d\n",ans);
}
return 0;
}