「HNOI2016」矿区

https://loj.ac/problem/2052

题解

平面图转对偶图。。

首先我们转的话需要给所有的平面标号,然后找到每条边看看他们隔开了哪两个平面。

做法就是对每个点维护它的所有排好序的出边,然后对于每一条有序边找到它的一条后继边。

如果一直找下去,就会找到一个平面,依次标号就好了。

我们转好了对偶图,\(dfs\)出对偶图的一颗生成树,然后对于一次询问,它肯定是切出了树上的一些联通块。

所以我们讨论一下每一条边的方向算一下答案就好了。

代码

#include<bits/stdc++.h>
#define N 200009
#define M 1200009
using namespace std;
typedef long long ll;
const double eps=1e-9;
int tot=1,n,m,q,pos[M],rt,b[N],nxt[M],num;
ll s[M],S[M],f[M];
bool ms[M],vis[M];
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();};
  return f?-x:x;
}
struct point{
  ll x,y;
  inline point operator +(const point &b)const{return point{x+b.x,y+b.y};}
  inline point operator -(const point &b)const{return point{x-b.x,y-b.y};}
  inline ll operator *(const point &b)const{return x*b.y-y*b.x;}
}a[N];
struct edge{
  int id,u,v;
  double ang;
  inline bool operator <(const edge &b)const{
      if(fabs(ang-b.ang)>eps)return ang<b.ang;
  }
}e[M];
vector<edge>vec[N];
vector<int>ed[M],ps[M];
inline void add(int x,int y){
  ++tot;e[tot]=edge{tot,x,y,atan2(a[y].y-a[x].y,a[y].x-a[x].x)};
  vec[x].push_back(e[tot]);
}
inline void build(){
    for(int i=1;i<=n;++i)sort(vec[i].begin(),vec[i].end());
    for(int i=2;i<=tot;++i){
      int id=e[i].v;
      vector<edge>::iterator it=lower_bound(vec[id].begin(),vec[id].end(),e[i^1]);
      if(it==vec[id].begin())it=vec[id].end();
      --it;nxt[i]=it->id;
    }
    for(int i=2;i<=tot;++i)if(!pos[i]){
      pos[i]=pos[nxt[i]]=++num;
      int x=nxt[i];
      while(1){ 
        if(e[x].v==e[i].u)break;
        s[num]+=(a[e[x].u]-a[e[i].u])*(a[e[x].v]-a[e[i].u]);
        x=nxt[x];pos[x]=num;
      }
      if(s[num]<=0)rt=num,s[num]=0;
    }
    for(int i=2;i<=tot;++i)ed[pos[i]].push_back(pos[i^1]),ps[pos[i]].push_back(i);
}
void dfs(int u,int fa){
    f[u]=fa;
    S[u]=s[u]*s[u];s[u]<<=1;vis[u]=1;
    for(int i=0;i<ed[u].size();++i){
      int v=ed[u][i],tg=ps[u][i];
      if(vis[v])continue;
      ms[tg]=ms[tg^1]=1;
      dfs(v,u);
      S[u]+=S[v];s[u]+=s[v];
    }
}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
void solve(){
  ll ans1=0,ans2=0;
  while(q--){
    int x=(rd()+ans1)%n+1;
    for(int i=1;i<=x;++i)b[i]=(rd()+ans1)%n+1;
    ans1=ans2=0;
    b[x+1]=b[1];
    for(int i=1;i<=x;++i){
        int xx=b[i],yy=b[i+1];
     //   cout<<xx<<" "<<ans1<<" "<<ans2<<"   ";
        edge z=edge{0,xx,yy,atan2(a[yy].y-a[xx].y,a[yy].x-a[xx].x)};
        vector<edge>::iterator it=lower_bound(vec[xx].begin(),vec[xx].end(),z);
        int j=it->id;
        if(!ms[j])continue;
        if(f[pos[j]]==pos[j^1])ans1+=S[pos[j]],ans2+=s[pos[j]];
        else ans1-=S[pos[j^1]],ans2-=s[pos[j^1]];
    }
    ll g=gcd(ans1,ans2);
    ans1/=g;ans2/=g;
    printf("%lld %lld\n",ans1,ans2);
  }
}
int main(){
  n=rd();m=rd();q=rd();
  int x,y;
  for(int i=1;i<=n;++i){
      x=rd();y=rd();
      a[i]=point{x,y};
  }
  for(int i=1;i<=m;++i){
      x=rd();y=rd();
      add(x,y);add(y,x);
  }
  build();dfs(rt,0);solve();
  return 0;
}
posted @ 2019-06-01 14:32  comld  阅读(371)  评论(0编辑  收藏  举报