[点分治 离线] BZOJ 4449 [Neerc2015]Distance on Triangulation

其实简单的找一条边然后分成两半
被我硬生生写成对偶图点分治分成三块 233
就把所有询问一起处理
点分治时 如果两个点在同一侧 就递归 否则对三个点bfs就能知道最短路径

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef pair<int,int> abcd;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=100005;

struct edge{
  int u,v,next;
}G[N<<3];
int head[N],head2[N],inum;
inline void add(int u,int v,int *head){
  int p=++inum; G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}
inline void link(int u,int v,int *head){
  add(u,v,head); add(v,u,head);
}
#define V G[p].v
int n;
int deg[N];
int pt[N][3];

map<abcd,int> ed;
inline void BT(){
  static int Q[N],l,r,rem[N];
  l=r=-1; cl(rem);
  for (int i=1;i<=n;i++) if (deg[i]==2) Q[++r]=i;
  for (int i=1;i<=n-2;i++){
    int u=Q[++l],a[3]; rem[u]=1;
    *a=0;
    for (int p=head[u];p;p=G[p].next)
      if (!rem[V])
    a[++*a]=V;
    pt[i][0]=u; pt[i][1]=a[1]; pt[i][2]=a[2];
    if ((--deg[a[1]])==2) Q[++r]=a[1];
    if ((--deg[a[2]])==2) Q[++r]=a[2];
  }
  for (int s=1;s<=n-2;s++){
    for (int i=0;i<3;i++)
      for (int j=0;j<i;j++){
    int u=pt[s][i],v=pt[s][j];
    if (u>v) swap(u,v);
    if (ed.count(abcd(u,v)))
      link(s,ed[abcd(u,v)],head2);
    else
      ed[abcd(u,v)]=s;
      }
  }
}

int del[N];

int sum,minv,rt;
int size[N];
inline void Root(int u,int fa){
  size[u]=1; int maxv=0;
  for (int p=head2[u];p;p=G[p].next)
    if (V!=fa && !del[V])
      Root(V,u),size[u]+=size[V],maxv=max(maxv,size[V]);
  maxv=max(maxv,sum-size[u]);
  if (maxv<minv) minv=maxv,rt=u;
}

int c[N],col=4;
int ins[N],clk;
int dis[3][N];

inline void bfs(int S,int *dis){
  static int Q[N],l,r;
  l=r=-1; ++clk;
  Q[++r]=S; dis[S]=0; ins[S]=clk;
  while (l<r){
    int u=Q[++l];
    for (int p=head[u];p;p=G[p].next)
      if (c[V]==c[u] && ins[V]!=clk){
    dis[V]=dis[u]+1;
    ins[V]=clk;
    Q[++r]=V;
      }
  }
}

int Col;
inline void color(int u,int fa){
  size[u]=1;
  for (int i=0;i<3;i++) c[pt[u][i]]=Col;
  for (int p=head2[u];p;p=G[p].next)
    if (V!=fa && !del[V])
      color(V,u),size[u]+=size[V];
}

struct event{
  int u,v,idx;
  event(int u=0,int v=0,int idx=0):u(u),v(v),idx(idx){ }
};

int Ans[N<<1];

inline void Divide(int u,vector<event> &que){
  if (que.empty()) return;
  del[u]=1;
  for (int i=0;i<3;i++)
    bfs(pt[u][i],dis[i]);
  Col=0;
  for (int p=head2[u];p;p=G[p].next)
    if (!del[V]){
      Col++;
      color(V,0);
    }
  for (int i=0;i<3;i++) c[pt[u][i]]=0;
  vector<event> vec[4];
  for (int i=0;i<(int)que.size();i++){
    int a=que[i].u,b=que[i].v,idx=que[i].idx;
    if (c[a]==0){
      for (int j=0;j<3;j++)
    if (a==pt[u][j])
      Ans[idx]=dis[j][b];
    }else if (c[b]==0){
      for (int j=0;j<3;j++)
    if (b==pt[u][j])
      Ans[idx]=dis[j][a];
    }else if (c[a]!=c[b]){
      Ans[idx]=1<<30;
      for (int j=0;j<3;j++)
    Ans[idx]=min(Ans[idx],dis[j][a]+dis[j][b]);
    }else{
      vec[c[a]].push_back(que[i]);
    }
  }
  for (int p=head2[u],i=0;p;p=G[p].next)
    if (!del[V]){
      Col=++col; color(V,0);
      sum=size[V]; minv=1<<30;
      Root(V,0);
      Divide(rt,vec[++i]);
    }
}

int main(){
  int iu,iv,Q;
  freopen("drive.in","r",stdin);
  freopen("drive.out","w",stdout);
  read(n);
  for (int i=1;i<n;i++) link(i,i+1,head);
  link(n,1,head);
  for (int i=1;i<=n;i++) deg[i]=2; 
  for (int i=1;i<=n-3;i++)
    read(iu),read(iv),link(iu,iv,head),deg[iu]++,deg[iv]++;
  BT();
  read(Q);
  vector<event> vec;
  for (int i=1;i<=Q;i++)
    read(iu),read(iv),vec.push_back(event(iu,iv,i));
  sum=n-2; minv=1<<30;
  Root(1,0);
  for (int i=1;i<=n;i++) c[i]=col;
  Divide(rt,vec);
  for (int i=1;i<=Q;i++)
    printf("%d\n",Ans[i]);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值