这题好神啊……
正解方向是分治,据我所知的分治方法有: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; }