983E - NN country

http://codeforces.com/problemset/problem/983/E

题意:给定一棵树,给m个公交车路线,即从u到v可直达不用换乘,给q次查询,问从u,v最少需要多少次换乘

题解:考虑到查询操作我们可以分成两部分u->lca(u,v)->v,对于u->lca这段路我们找到t,即从t->lca只需要一次换乘,对于右边路径,我们需要找到一个点z,使得t->z有一次换乘的路径(通过主席树来维护t的子树和z的子树之间是否有直达路线),满足单调性,所以二分查找最合适的z,然后加上前半部分的贡献即为所求

#include <bits/stdc++.h>
#define ll long long
#define s second
#define f first
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
const int MAXN=2e5+10;
const int K=20;
using namespace std;
vector<int>vec[MAXN];
int num[MAXN],son[MAXN],fa[MAXN],dep[MAXN],f[MAXN];
int st[MAXN][21];
int n,m,q;
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-‘0‘,ch=getchar();
    return f*x;
}
void dfs1(int v,int pre,int deep){
	num[v]=1;fa[v]=pre;dep[v]=deep+1;
	for(int i=0;i<vec[v].size();i++){
		int u=vec[v][i];
		if(u!=pre){
			dfs1(u,v,deep+1);
			num[v]+=num[u];
			if(son[v]==-1||num[son[v]]<num[u])son[v]=u;
		}
	}
}
int p[MAXN],fp[MAXN],cnt,tp[MAXN],rt[MAXN];
void dfs2(int v,int td){
	p[v]=++cnt;fp[cnt]=v;tp[v]=td;
	if(son[v]!=-1) dfs2(son[v],td);
	for(int i=0;i<vec[v].size();i++)if(son[v]!=vec[v][i]&&vec[v][i]!=fa[v])dfs2(vec[v][i],vec[v][i]);
}
int Lca(int u,int v){
	int uu=tp[u];int vv=tp[v];
	while(uu!=vv){
		if(dep[uu]<dep[vv]) swap(uu,vv),swap(u,v);
		u=fa[uu];uu=tp[u];
	}
	if(dep[u]>dep[v]) swap(u,v);
	return u;
}
typedef struct node{
	int l,r,sum;
}node;
node d[MAXN*45];int cnt1;
int querty(int u,int v){
	if(u==v) return 0;
	if(dep[st[v][K]]>dep[u]) return -1;
	int ans1=1,t=v;
//	cout<<p[st[v][K]]<<" "<<p[u]<<endl;
	for(int i=K;i>=0;i--){
	if(dep[st[t][i]]>dep[u])ans1+=(1<<i),t=st[t][i];
//	cout<<p[st[t][i]]<<" "<<p[u]<<endl;
}
	return ans1;
}
void update(int &x,int y,int l,int r,int t){
//	cout<<l<<":::"<<r<<" "<<t<<endl;
	x=++cnt1;d[x]=d[y];d[x].sum++;
//	cout<<d[x].sum<<endl;
	if(l==r) return ;
	int mid=(l+r)>>1;
	if(t<=mid) update(d[x].l,d[y].l,l,mid,t);
	else update(d[x].r,d[y].r,mid+1,r,t);
}
int ans=0;
void Sum(int x,int y,int l,int r,int ql,int qr){
//	cout<<l<<" "<<r<<" "<<ans<<" "<<d[y].sum<<endl;
	if(ql<=l&&r<=qr){ans+=(d[y].sum-d[x].sum);return ;}
	int mid=(l+r)>>1;
	if(ql<=mid) Sum(d[x].l,d[y].l,l,mid,ql,qr);
	if(qr>mid) Sum(d[x].r,d[y].r,mid+1,r,ql,qr);
}
int erfen(int v,int mid){
	int vv=tp[v];
	while(1){
		int t=p[v]-p[vv];
		if(mid<=t) return fp[p[v]-mid];
		mid-=(t+1);v=fa[vv];vv=tp[v];
	}
}
bool check(int u,int v){if(p[v]>p[u])swap(v,u);ans=0;Sum(rt[p[v]-1],rt[p[v]+num[v]-1],1,n,p[u],p[u]+num[u]-1);return ans>0;}
int slove(int u,int v){
	if(u==v) return 0;
	int lca=Lca(u,v);
	if(lca==u||lca==v){
		if(dep[u]>dep[v]) swap(u,v);
		return querty(u,v);
	}
	if(dep[st[u][K]]>dep[lca]) return -1;
	int ans3=0,t=u;
	for(int i=K;i>=0;i--)if(dep[st[t][i]]>dep[lca])ans3+=(1<<i),t=st[t][i];
//	cout<<ans3<<endl;
	int l=0;int r=dep[v]-dep[lca]-1;int z=dep[v]-dep[lca];
	//cout<<l<<" "<<r<<endl;
//	cout<<"::::"<<t<<endl;
	while(l<=r){
		int mid=(l+r)>>1;
	//	cout<<t<<"---"<<erfen(v,mid)<<endl;
//		cout<<l<<" "<<r<<" "<<erfen(v,mid)<<endl;
		if(check(t,erfen(v,mid)))z=mid,r=mid-1;
		else l=mid+1;
//		cout<<l<<" "<<r<<" "<<z<<endl;
	}
//	cout<<erfen(v,z)<<endl;
	int ttt=querty(erfen(v,z),v);
//	cout<<ans3<<" "<<ttt<<endl;
	if(ttt<0) return -1;
	ans3+=(ttt+1);
	return ans3;
}
int find1(int x){
//	cout<<x<<"----"<<endl;
	if(x==f[x]) return x;
	else return f[x]=find1(f[x]);
}
void dfs3(int v){
	for(int i=1;i<=K;i++) st[v][i]=st[st[v][i-1]][i-1];
	for(int i=0;i<vec[v].size();i++)if(vec[v][i]!=fa[v])dfs3(vec[v][i]);
}
void merge(int u,int v){
//	cout<<dep[u]<<" "<<dep[v]<<" "<<u<<" "<<v<<endl;
for(u=find1(u);dep[u]>=dep[v];u=find1(u))st[u][0]=v,f[u]=fa[u];
}
typedef struct Node{
	int x,y,z;
}Node;
Node que[MAXN];
bool cmp1(Node aa,Node bb){
	return dep[aa.z]<dep[bb.z];
}
bool cmp2(Node aa,Node bb){
	return p[aa.x]<p[bb.x];
}
int main(){
	n=read();
	//m=read();q=read();
	int u,v;cnt=cnt1=0;
	for(int i=1;i<=n;i++)son[i]=-1,st[i][0]=f[i]=i;
	//for(int i=1;i<n;i++)u=read(),v=read(),vec[u].push_back(v),vec[v].push_back(u);
	inc(i,2,n)u=read(),vec[i].push_back(u),vec[u].push_back(i);
	dfs1(1,0,0);dfs2(1,1);
//	for(int i=1;i<=n;i++) cout<<p[i]<<" ";
//	cout<<endl;
	m=read();
	for(int i=1;i<=m;i++){
	u=read();v=read();
	if(u==v) continue;
	que[i].x=u,que[i].y=v,que[i].z=Lca(que[i].x,que[i].y);
}
	sort(que+1,que+m+1,cmp1);
	for(int i=1;i<=m;i++)merge(que[i].x,que[i].z),merge(que[i].y,que[i].z);
	for(int i=1;i<=m;i++)if(p[que[i].x]>p[que[i].y])swap(que[i].x,que[i].y);
//	cout<<"sb"<<endl;
//	for(int i=1;i<=n;i++) cout<<st[i][0]<<" ";
//	cout<<endl;
	dfs3(1);
	sort(que+1,que+1+m,cmp2);int t=1;
	for(int i=1;i<=cnt;i++){
		rt[i]=rt[i-1];
		for(;p[que[t].x]==i;t++)update(rt[i],rt[i],1,n,p[que[t].y]);
//		ans=0;Sum(rt[3],rt[4],1,n,5,6);
//		cout<<ans<<"==---"<<endl;
	}
//		ans=0;Sum(rt[3],rt[4],1,n,5,6);
//		cout<<ans<<"==---"<<endl;
//	cout<<"sb"<<endl;
	q=read();
	while(q--){
		u=read();v=read();
		int ans2=slove(u,v);
		if(ans2<0) puts("-1");
		else printf("%d\n",ans2);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/wang9897/p/9049003.html

时间: 2024-10-09 15:22:28

983E - NN country的相关文章

Codeforces 983E NN country 思维 (看题解)

NN country 是1175E的加强树上版本, 大致思路是一样的.. 难点在于判断两个点是否被同一条线覆盖.. 居然没想出来. 我们先把所有点对都离线,对于点对(u, v) 我们dfs到 u 的时候 记录一下v子树的和为 t1, 然后把所有在 u 的线段的另一端 + 1, 向子树递归, 回溯的时候再求一下 v 子树的和为 t2 只要判断t1 是否等于 t2, 就知道有没有一条线段同时覆盖u,v. #include<bits/stdc++.h> #define LL long long #d

CF983E NN country [倍增][LCA][树状数组]

题意: $n$个城市,从$1$到$n$标号,$n$个城市构成一棵树. 有$m$条双向公交路线,对于每条路线,公交沿着两个终点站之间的最短路径行驶并会在沿途各站停车.从一个城市只能坐公交前往其他城市. 有$q$个询问:从一个城市到另一个城市要搭乘多少趟公交?不能到达输出$-1$. 对于每个询问$x,y$,求出$z=lca(x,y)$. 先从$x$和$y$出发到达$z$下方的城市$x'$和$y'$使得再坐一趟车可到$z$,记步数和为$s$.倍增,预处理$f[i][j]$表示从$i$出发坐$2^{j}

Codeforces Round #483 (Div. 1) 简要题解

来自FallDream的博客,未经允许,请勿转载,谢谢. 为了证明一下我又来更新了,写一篇简要的题解吧. 这场比赛好像有点神奇,E题莫名是道原题,导致有很多选手直接过掉了(Claris 表演24s过题).然而D题比E题要难一些,分还少. A. Finite or not? 先把\(\frac{p}{q}\)约成最简分数,然后就是要判断是否\(q\)的所有质因数都是\(b\)的质因数. 每次取\(g=gcd(b,q)\),并尽可能的让\(q\)除\(g\),最后判断\(q\)是否是1即可. 还有一

hibernate 单向 n-n

域模型: 关系数据模型 n-n 的关联必须使用连接表 与 1-n 映射类似,必须为 set 集合元素添加 key 子元素,指定 CATEGORIES_ITEMS 表中参照 CATEGORIES 表的外键为 CATEGORIY_ID. 与 1-n 关联映射不同的是,建立 n-n 关联时, 集合中的元素使用 many-to-many. many-to-many 子元素的 class 属性指定 items 集合中存放的是 Item 对象, column 属性指定 CATEGORIES_ITEMS 表中

hibernate 双向n-n

域模型: 关系数据模型 双向 n-n 关联需要两端都使用集合属性 双向n-n关联必须使用连接表 集合属性应增加 key 子元素用以映射外键列, 集合元素里还应增加many-to-many子元素关联实体类 在双向 n-n 关联的两边都需指定连接表的表名及外键列的列名. 两个集合元素 set 的table 元素的值必须指定,而且必须相同.set元素的两个子元素:key 和 many-to-many 都必须指定 column 属性,其中,key 和 many-to-many 分别指定本持久化类和关联类

HDU5723 Abandoned country 最小生成树+深搜回溯法

Description An abandoned country has n(n≤100000) villages which are numbered from 1 to n. Since abandoned for a long time, the roads need to be re-built. There are m(m≤1000000) roads to be re-built, the length of each road is wi(wi≤1000000). Guarante

MySQL Workbench建表时 PK NN UQ BIN UN ZF AI 的含义

PK==>  PRIMARY KEY    ==> 主键NN==>  NOT NULL       ==> 不可为空UQ==>  UNIQUE         ==> 唯一的AL==>  AUTO INCREMENT ==> 自动增加BIN==> BINARY         ==> 二进制UN==>  UNSIGNED       ==>无符号位的ZF==>  ZERO FILL      ==>补零 PK Belong

hdu 5723 Abandoned country 最小生成树+子节点统计

Abandoned country Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 3006    Accepted Submission(s): 346 Problem Description An abandoned country has n(n≤100000) villages which are numbered from 1

HDU计算机学院大学生程序设计竞赛(2015’12)The Country List

The Country List Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2598    Accepted Submission(s): 615 Problem Description As the 2010 World Expo hosted by Shanghai is coming, CC is very honorable