【BZOJ 4449】[Neerc2015]Distance on Triangulation 多边形分治结构

这题好神啊……
正解方向是分治,据我所知的分治方法有:I.离线后直接对多边形以及所有的询问进行分治 II.建立多边形的分治结构(对于三角形来说类似线段树,对于对角线来说类似平衡树),然后每次在这个分治结构上进行查询 III.将原图转为其对偶图(利用拓扑),发现是一棵树,然后在这棵树上进行分治(似乎也有离线分治和在线建立分治结构两种方法)
我用的是第二种方法,感觉写起来不是很容易,但是也并不恶心,具体实现以及具体问题的处理方法见代码.
感觉这样分治的复杂度是log的,实际证明最坏情况下存在使得任意一侧的三角形数不少于n/3的分发,然而并不会证,大该感性理解一下吧.
思维笔记:I.分治无处不在 II.分治就是分治,也可以没有信息的合并 III.分治的出发点也可以是砍半,就像二分一样 IV.分治结构的建立类似分治,而分治结构的使用更像是二分
算法笔记:I.建立分治结构时所需信息,以及分治结构所需维护的信息,是不一样的,分开考虑与处理会方便得多 II.在分治结构中,储存信息的方式可以是对于每个点存储其在每一层的信息,也可以是对于每一层存储每个点的信息,两者各有千秋,在这道题里,个人感觉前者用起来更加方便 III.这道题分治的理由我感觉是——一个对角线把多边形切成两个部分,如果询问的两个点都在这两个部分里的其中一个里面,那这次询问一定与另一部分无关

#pragma GCC optimize("O3")
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define pb push_back
#define ft first
#define sd second
#define mmp(a,b) (std::make_pair(a,b))
char xB[(1<<15)+10],*xS,*xT;
#define gtc (xS==xT&&(xT=((xS=xB)+fread(xB,1,1<<15,stdin)),xS==xT)?0:*xS++)
inline void read(int &x){
  register char ch=gtc;
  for(x=0;ch<'0'||ch>'9';ch=gtc);
  for(;ch>='0'&&ch<='9';x=(x<<1)+(x<<3)+ch-'0',ch=gtc);
}
typedef std::pair<int,int> pii;
typedef std::vector<int> vi;
typedef std::vector<pii> vii;
const int A=30,N=52000,Inf=0x3f3f3f3f;
vi tmp1;
vii tmp2;
struct Block{
  Block *ch[2];
  pii cut;
}*root,block[N<<2];
#define newblock (block+(sz++))
int to[N][A],dis[N][A][2];
int q[N],front,back,vis[N],id[N];
int n;
struct V{int to,next;}c[N<<2];
int head[N],t;
inline void add(int x,int y){
  c[++t].to=y,c[t].next=head[x],head[x]=t;
  c[++t].to=x,c[t].next=head[y],head[y]=t;
}
int sz,cnt;
inline void bfs(int S,int deep,int opt){
  ++cnt,front=back=0,q[back++]=S;
  dis[S][deep][opt]=0,vis[S]=cnt;
  register int x,i;
  while(front!=back){
    x=q[front++];
    for(i=head[x];i;i=c[i].next)
      if(vis[c[i].to]!=cnt){
        vis[c[i].to]=cnt;
        dis[c[i].to][deep][opt]=dis[x][deep][opt]+1;
        q[back++]=c[i].to;
      }
  }
}
inline void build(Block *&p,register vi poi,register vii cut,int deep){
  p=newblock;
  if(cut.size()==0)return;
  pii mini;
  int min=Inf,temp,size1=poi.size(),size2=cut.size();
  register int i;
  for(i=0;i<size2;++i){
    temp=std::abs(id[cut[i].ft]-id[cut[i].sd])+1;
    temp=std::max(temp,size1-temp+2);
    if(temp<min)
      min=temp,mini=cut[i];
  }
  p->cut=mini;
  int l=id[mini.ft],r=id[mini.sd];
  if(l>r)std::swap(l,r);
  tmp1.clear(),tmp2.clear();
  for(i=0;i<=l;++i){
    tmp1.pb(poi[i]);
    id[poi[i]]=tmp1.size()-1;
    to[poi[i]][deep]=1;
  }
  for(i=r;i<size1;++i){
    tmp1.pb(poi[i]);
    id[poi[i]]=tmp1.size()-1;
    to[poi[i]][deep]=1;
  }
  for(i=0;i<size2;++i){
    if(cut[i]==mini)continue;
    if(to[cut[i].ft][deep]==1&&to[cut[i].sd][deep]==1)
      tmp2.pb(cut[i]);
  }
  build(p->ch[1],tmp1,tmp2,deep+1);
  tmp1.clear(),tmp2.clear();
  for(i=l;i<=r;++i){
    tmp1.pb(poi[i]);
    id[poi[i]]=i-l;
    to[poi[i]][deep]=0;
  }
  for(i=0;i<size2;++i){
    if(cut[i]==mini)continue;
    if(to[cut[i].ft][deep]==0&&to[cut[i].sd][deep]==0)
      tmp2.pb(cut[i]);
  }
  build(p->ch[0],tmp1,tmp2,deep+1);
  for(i=0;i<size1;++i)head[poi[i]]=0;
  t=0;
  for(i=1;i<size1;++i)
    add(poi[i],poi[i-1]);
  add(poi[size1-1],poi[0]);
  for(i=0;i<size2;++i)
    add(cut[i].ft,cut[i].sd);
  bfs(mini.ft,deep,0);
  bfs(mini.sd,deep,1);
}
inline int query(Block *p,int x,int y,int deep){
  if(!p->ch[0])return 1;
  if(p->cut.ft==x)return dis[y][deep][0];
  if(p->cut.sd==x)return dis[y][deep][1];
  if(p->cut.ft==y)return dis[x][deep][0];
  if(p->cut.sd==y)return dis[x][deep][1];
  if(to[x][deep]==to[y][deep])return query(p->ch[to[x][deep]],x,y,deep+1);
  int ret=dis[x][deep][0]+dis[y][deep][0];
  ret=std::min(ret,dis[x][deep][1]+dis[y][deep][1]);
  return ret;
}
int main(){
  read(n);
  register int i;pii rio;
  for(i=1;i<=n;++i)
    tmp1.pb(i),id[i]=i-1;
  for(i=1;i<=n-3;++i)
    read(rio.ft),read(rio.sd),tmp2.pb(rio);
  build(root,tmp1,tmp2,1);
  int T,x,y;
  read(T);
  while(T--){
    read(x),read(y);
    printf("%d\n",x==y?0:query(root,x,y,1));
  }
  return 0;
}

 

posted @ 2018-03-11 09:00  TS_Hugh  阅读(452)  评论(0编辑  收藏  举报