【数学】省选四校联考 开开车

在这里插入图片描述


分析:

这题超有(e)趣(xin)的。。。(又是一道结论题。。。)

结论:对于一个三角划分(即题目中的划分方式),每次选择一条划分的边,将全图分为两个多边形,再各自选择一条划分的边,递归操作,一定存在某种方案,使得在很小的层数内全部划分为三角形。。。

有了这个结论,这题就很好做了。

可以每次选择那条边后,其左侧的点到达右侧的点一定经过这两个点中的一个,因此只需要把这两个点到达所有其他点的最短路求出来。

询问的时候,一直找到将询问点分为两部分的那一层,然后直接回答即可。

复杂度 O ( ( N + Q ) ∗ 层 数 ) O((N+Q)*层数) O((N+Q))

关于这个层数的数量级,博主不严谨地证明认为应该是 l o g 3 2 N log_{\frac 3 2}^N log23N的级别,如果读者有严谨证明,请务必告诉我。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#define SF scanf
#define PF printf
#define MAXN 100010
#define MAXM 200000
using namespace std;
struct node{
	map<int,int> S;
	vector<int> v;
	vector<vector<int> >a;
	vector<pair<int,int> > edge;
	vector<int> distl,distr;
	int L,R;
	int pl,pr;
	pair<int,int> best_split(){
		int d=a.size()+1;
		pair<int,int> res;
		for(int i=0;i<int(edge.size());i++){
			int l=max(edge[i].second-edge[i].first,int(a.size())-edge[i].second+edge[i].first);
			if(l<d){
				d=l;
				res=make_pair(edge[i].first,edge[i].second);	
			}
		}
		return res;
	}
	void add_point(int x){
		v.push_back(x);
		S[x]=int(v.size())-1;
	}
	void add_edge(int u,int v){
		u=S[u],v=S[v];
		if(u>v) swap(u,v);
		edge.push_back(make_pair(u,v));
		a[u].push_back(v);
		a[v].push_back(u);
	}
}tree[MAXM];
queue<int> q;
void bfs(int stx,vector<int> &dist,vector<vector<int> > &a){
	q.push(stx);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=0;i<int(a[x].size());i++){
			int u=a[x][i];
			if(u!=stx&&dist[u]==0){
				dist[u]=dist[x]+1;
				q.push(u);
			}
		}
	}
}
int ncnt;
void build(int id){
	if(tree[id].a.size()<=3)
		return ;
	pair<int,int> tmp=tree[id].best_split();
	int L=tmp.first,R=tmp.second;
	int pl=++ncnt,pr=++ncnt;
	int Siz=tree[id].v.size();
	tree[id].distl.resize(Siz);
	tree[id].distr.resize(Siz);
	bfs(L,tree[id].distl,tree[id].a);
	bfs(R,tree[id].distr,tree[id].a);
	tree[id].L=L,tree[id].R=R;
	tree[id].pl=pl,tree[id].pr=pr;
	int sizl=(R-L+1);
	int sizr=Siz-sizl+2;
	tree[pl].a.resize(sizl);
	tree[pr].a.resize(sizr);
	for(int i=L;i<=R;i++)                tree[pl].add_point(tree[id].v[i]);
	for(int i=0;i<=L;i++)                tree[pr].add_point(tree[id].v[i]);
	for(int i=R;i<tree[id].v.size();i++) tree[pr].add_point(tree[id].v[i]);
	for(int i=0;i<tree[id].edge.size();i++){
		int u=tree[id].edge[i].first;
		int v=tree[id].edge[i].second;
		if(u>=L&&v<=R)
			tree[pl].add_edge(tree[id].v[u],tree[id].v[v]);
		if((u<=L&&v<=L)||(u<=L&&v>=R)||(u>=R&&v>=R))
			tree[pr].add_edge(tree[id].v[u],tree[id].v[v]);
	}
	build(pl);
	build(pr);
}
int query(int id,int x,int y){
	if(tree[id].v.size()==3)
		return 1;
	int u=tree[id].S[x],v=tree[id].S[y];
	if(u>v) swap(u,v);
	int L=tree[id].L,R=tree[id].R;	
	if(u>=L&&u<=R){
		if(v>=L&&v<=R)
			return query(tree[id].pl,x,y);
		return min(tree[id].distl[u]+tree[id].distl[v],tree[id].distr[u]+tree[id].distr[v]);
	}
	if(u<=L||u>=R){
		if(v<=L||v>=R)
			return query(tree[id].pr,x,y);
		return min(tree[id].distl[u]+tree[id].distl[v],tree[id].distr[u]+tree[id].distr[v]);
	}
}
int main(){
//	freopen("dri");
	int n,u,v,m;
	SF("%d",&n);
	tree[1].a.resize(n);
	for(int i=1;i<=n;i++) tree[1].add_point(i);
	for(int i=1;i<n;i++)
		tree[1].add_edge(i,i+1);
	tree[1].add_edge(1,n);
	for(int i=1;i<=n-3;i++){
		SF("%d%d",&u,&v);
		if(u>v) swap(u,v);
		tree[1].add_edge(u,v);
	}
	ncnt=1;
	build(1);
	SF("%d",&m);
	int x,y;
	for(int i=1;i<=m;i++){
		SF("%d%d",&x,&y);
		if(x==y)
			PF("0\n");
		else
			PF("%d\n",query(1,x,y));	
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值