HNOI2016 矿区 题解
[HNOI2016] 矿区 Solution
神仙计算几何加图论题。
题目大意:给定一个连通平面图,平面图被分成很多个面,每次询问一个多边形的贡献,贡献是其中每个面的面积的平方和除以面积和。
解题思路:
我们需要把一个多边形内的每个面一起算,可以考虑对每个面之间连边,建出一棵生成树。
实际上就是平面图转成对偶图。对偶图:把原平面图的每个面看作一个点,把原图上有邻边的面在新图上对应的点连边。
首先我们需要找出每个面包含哪些边。对于一个点,把它的边按极角排序后,相邻的边就同属一个多边形。可以先枚举一条初始边,然后逆时针走向下一条极角比它小的边,直到走回初始边,这样就找到了一个面。
同时我们可以求这个多边形的面积与面积的平方,使用向量叉积求面积。由于我们是逆时针求,所以叉积是正的。
然后连边,首先每条边属于两个面,给这两个面连边即可。我们也可以知道面的个数是 \(O(m)\) 的。
假如我们已经建出了生成树,那么每次询问一个多边形时,可以想象这个生成树会穿过多边形,对于一条进入多边形的树边就加上子树和,对于一条走出多边形的树边就减去子树和,发现我们就可以处理询问了。
但我们发现,这要求树根在多边形外。而刚好,我们求面时,会求到原平面图外范围无限的面(绕边界顺时针走),且由于是顺时针走的,所以面积叉积是负的,我们能容易地找到它。我们以这个无限的面为根即可。可以一次 DFS 求出生成树。
时间复杂度 \(O((m+d)\log m)\)。
细节:
- 叉积算面积需要除以二,而对应的面积的平方会除以四,我们可以在最后算答案时,给分母乘二即可。
- 不要使用
map或unordered_map查找一个点上连的边,或者查找两个点之间连的边,那样会 TLE。最好用vector存下来排序,然后二分查找。
const int N=2e5+5,M=6e5+5;
struct point{int x,y;};
int operator^(point a,point b){return a.x*b.y-b.x*a.y;}
int n,m,K,X[N],Y[N];
struct edge{int to,num; ld rad;};
int find(vector<edge> &x,ld rad){
int l=0,r=x.size()-1,ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(x[mid].rad>rad)l=mid+1;
else ans=mid,r=mid-1;
}
return ans;
}
vector<edge> G[N];
int tot,side[M*2],bz[M*2];
ll s1[M*2],s2[M*2];
int root,vis[M*2],fa[M*2];
vector<int> g[M*2];
void dfs(int x){
vis[x]=1;
for(int v:g[x])if(!vis[v]){
fa[v]=x;
dfs(v);
s1[x]+=s1[v],s2[x]+=s2[v];
}
}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
unordered_map<int,int> mp[N];
int add[M*2],del[M*2];
signed main(){
read(n,m,K);
fo(i,1,n)read(X[i],Y[i]);
fo(i,1,m){
int u,v; read(u,v);
G[u].push_back((edge){v,i*2-1,atan2l(Y[v]-Y[u],X[v]-X[u])});
G[v].push_back((edge){u,i*2,atan2l(Y[u]-Y[v],X[u]-X[v])});
mp[u][v]=i*2-1,mp[v][u]=i*2;
}
fo(i,1,n)sort(G[i].begin(),G[i].end(),[](edge a,edge b){return a.rad>b.rad;});
fo(i,1,n)for(auto j:G[i])if(!bz[j.num]) {
bz[j.num]=1,side[j.num]=++tot;
int x=j.to,from=i;
s1[tot]+=point{X[i],Y[i]}^point{X[x],Y[x]};
while(x!=i){
int at=find(G[x],atan2l(Y[from]-Y[x],X[from]-X[x]));
if(++at==G[x].size())at=0;
bz[G[x][at].num]=1,side[G[x][at].num]=tot;
from=x,x=G[x][at].to;
s1[tot]+=point{X[from],Y[from]}^point{X[x],Y[x]};
}
if(s1[tot]<=0)root=tot;
s2[tot]=s1[tot]*s1[tot];
}
fo(i,1,m){
int u=side[i*2-1],v=side[i*2];
g[u].push_back(v),g[v].push_back(u);
}
dfs(root);
ll lastans=0;
fo(i,1,K){
int c; read(c);
c=(c+lastans)%n+1;
int d; read(d);
d=(d+lastans)%n+1;
int d1=d,t1=0,t2=0;
fo(j,1,c-1){
int t; read(t);
t=(t+lastans)%n+1;
int u=side[mp[d][t]],v=side[mp[t][d]];
if(fa[u]==v)add[++t1]=u;
else if(fa[v]==u)del[++t2]=v;
d=t;
}
int u=side[mp[d][d1]],v=side[mp[d1][d]];
if(fa[u]==v)add[++t1]=u;
else if(fa[v]==u)del[++t2]=v;
sort(add+1,add+t1+1),sort(del+1,del+t2+1);
t1=unique(add+1,add+t1+1)-add-1,t2=unique(del+1,del+t2+1)-del-1;
ll S1=0,S2=0;
fo(i,1,t1)S1+=s1[add[i]],S2+=s2[add[i]];
fo(i,1,t2)S1-=s1[del[i]],S2-=s2[del[i]];
S1*=2;
ll cd=gcd(S1,S2);
S1/=cd,S2/=cd;
write(S2,' ',S1,'\n');
lastans=S2;
}
return 0;
}

浙公网安备 33010602011771号