1. 程式人生 > >JZOJ-senior-5918. 【NOIP2018模擬10.20】Car

JZOJ-senior-5918. 【NOIP2018模擬10.20】Car

Time Limits: 1000 ms Memory Limits: 524288 KB

Description

作為申國的學生,你正在進行時事政治學習。 1.美國前國務卿是 A.希拉前 B. 希拉後 C.希拉里 D.希拉外 2.現任日本首相是 A.安倍晉一 B. 安倍晉二 C. 安倍晉三 D. 安倍晉四 3.現任申國國家元首是 A.吸壽瓶 B. 吸權瓶 C. 吸金瓶 D. 簾刃瓶 由於題目太難你不會做,你只好思考幾天後的旅行問題。 有若干個城市形成一棵樹的形狀。 有若干輛雙向班車往返於兩個城市(中途經過的城市都會停)。 有人要從城市x到城市y,問最少坐幾趟班車。 如果到不了,輸出-1。

Input

第一行n,表示城市數。 第二行n-1個數,第i個數表示第i+1個城市的父親,保證小於等於i。 第三行一個整數m,表示班車數。 接下來m行,每行兩個整數表示一條班車的起點城市和終點城市。 接下來一個整數q,表示詢問數。 接下來q行每行兩個整數表示詢問從一個城市到另一個城市。

Output

對於每個詢問,輸出最少用多少車到達。

Sample Input

7 1 1 1 4 5 6 4 4 2 5 4 1 3 6 7 6 4 5 3 5 7 2 4 5 3 2 5 3

Sample Output

1 3 -1 1 2 3

Data Constraint

對於20%:n<=10 對於50%:n<=1000 對於100%:n<=200000 m,q的範圍與n不會相差太多。 捆綁測試。

Solution

20號的題目拖到了現在…… 我提交上去在OJ上爆棧了,一直沒改,今天……它終於過了 最少坐幾趟班車?那天早上做比賽的時候就覺得這題和之前做的某題特別像,之前的題目也是應用倍增的思想處理,但是是一條軸,非常好處理,而這是一棵樹,由於沒想到怎麼處理轉折點坐幾趟車,於是蒟蒻我就只剩暴力分了

預處理

首先,我們對於每個節點預處理出從它出發向上乘一次車最遠能到哪,對於節點 xx ,它向上坐一趟車最遠能到達的位置顯然就是從它和它子樹中出發向上一次最遠能到達的位置,這個很好處理, dfsdfs 一遍就好

然後,我們處理每個節點往上坐 2j2^j 趟車最遠能到達的位置,這是個貪心的策略

求答案

接著,對於每個詢問,我們求出 z=lca(x,y)z=lca(x,y) ,求出 x,yx,y 分別能到達的比 zz 深度要大的第一個位置 x1,y1x1,y1 ,也就是說,從 x1,y1x1,y1 出發再坐一趟車就能到達或超越 zz ,記錄坐車的次數 ansans

這時候出現了三種情況

  1. 跳不到 zz ,ans=-1
  2. 有車覆蓋路徑 (x1,y1)(x1,y1) ,ans++
  3. 沒有車覆蓋路徑 (x1,y1)(x1,y1),ans+=2

那麼,如何知道有沒有車覆蓋這條路徑呢? 第一種情況 這兩個點在一條鏈上,那麼我們只需要看下面那個點能不能t跳到上面去即可 第二種情況 我們要判斷是否有車從 x1x1 的子樹裡到 y1y1 的子樹裡,在進入 x1x1 前記錄一下能到 y1y1 子樹裡的車的數量,記為 pp ,在做完 x1x1 的子樹後我們查詢一下能到 y1y1 子樹裡的車的數量,記為 qq ,如果 p&lt;qp&lt;q ,顯然就是可以只乘一次車從 x1x1y1y1 ,否則就要乘兩次車了(由於 dfsdfs 序十分好用,我們利用它和掃描線的思想搞一搞就好了)

Code

#include<algorithm>
#include<cstdio>
#include<vector>

#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)

using namespace std;

const int N=2e5+5;
int n,m,q,num,time;
int c[N],L[N],R[N],dep[N],last[N],_2[20];
int Ans[N],Sum[N],f[N][20],low[N][20];
struct edge{int to,next;}e[2*N];
struct node{int x,k;}R1,R2;
vector<int> Bus[N],up[N];
vector<node> Q[N];

void link(int x,int y)
{
	e[++num]=(edge){y,last[x]},last[x]=num;
}

void add(int x)
{
	for(;x<=n;x+=x&(-x)) ++c[x];
}

int ask(int x)
{
	int s=0;
	for(;x;x-=x&(-x)) s+=c[x];
	return s;
}

void dfs1(int x)
{
	L[x]=++time,dep[x]=dep[f[x][0]]+1;
	for(int w=last[x];w;w=e[w].next)
		if(e[w].to!=f[x][0]) dfs1(e[w].to);
	R[x]=time;
}

void dfs2(int x)
{
	for(int w=last[x];w;w=e[w].next)
		if(e[w].to!=f[x][0])
		{
			dfs2(e[w].to);
			if(dep[low[e[w].to][0]]<dep[low[x][0]])
				low[x][0]=low[e[w].to][0];
		}
}

int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	fd(i,18,0) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
	if(x==y) return x;
	fd(i,18,0) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}

node path(int x,int top)
{
	if(x==top) return (node){x,0};
	int w=0;
	fd(i,18,0) if(dep[low[x][i]]>dep[top]) x=low[x][i],w+=_2[i];
	if(low[x][0]==x) return (node){x,-1};
	return (node){x,w};
}

int main()
{
	freopen("car.in","r",stdin);
	freopen("car.out","w",stdout);
	_2[0]=1;
	fo(i,1,18) _2[i]=_2[i-1]<<1;
	scanf("%d",&n);
	fo(i,2,n) scanf("%d",&f[i][0]),link(f[i][0],i);
	
	dfs1(1);
	fo(i,1,18)
		fo(x,1,n)
			f[x][i]=f[f[x][i-1]][i-1];
			
	scanf("%d",&m);
	fo(i,1,m)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		if(x==y) continue;
		int z=lca(x,y);
		if(L[x]>L[y]) swap(x,y);
		Bus[L[x]].push_back(L[y]);
		if(dep[z]<dep[x]) up[x].push_back(z);
		if(dep[z]<dep[y]) up[y].push_back(z);
	}
	
	fo(x,1,n)
	{
		low[x][0]=x;
		for(int i=0;i<up[x].size();++i)
			if(dep[up[x][i]]<dep[low[x][0]])
				low[x][0]=up[x][i];
	}
	dfs2(1);
	fo(j,1,18)
		fo(i,1,n)
			low[i][j]=low[low[i][j-1]][j-1];
			
	scanf("%d",&q);
	fo(i,1,q)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		if(x==y) {Ans[i]=0; continue;}
		int z=lca(x,y);
		R1=path(x,z),R2=path(y,z);
		if(R1.k<0||R2.k<0) {Ans[i]=-1; continue;}
		if(R1.x==z||R2.x==z) {Ans[i]=R1.k+R2.k+1; continue;};
		Ans[i]=R1.k+R2.k+2,x=R1.x,y=R2.x;
		if(L[x]>L[y]) swap(x,y);
		Q[L[x]-1].push_back((node){y,i});
		Q[R[x]].push_back((node){y,-i});
	}
	
	fo(i,1,n)
	{
		for(int j=0;j<Bus[i].size();++j) add(Bus[i][j]);
		for(int j=0;j<Q[i].size();++j)
		{
			int w=Q[i][j].k>0?1:-1;
			int x=Q[i][j].x,id=Q[i][j].k*w;
			Sum[id]+=w*(ask(R[x])-ask(L[x]-1));
		}
	}
	fo(i,1,q) printf("%d\n",Ans[i]-(Sum[i]<0));
}