zoukankan      html  css  js  c++  java
  • 「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
    ",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;
    }
    
  • 相关阅读:
    Dot Net WinForm 控件开发 (七) 为属性提下拉式属性编辑器
    WinForm 程序的界面多语言切换
    c#遍历HashTable
    Dot Net WinForm 控件开发 (三) 自定义类型的属性需要自定义类型转换器
    Dot Net WinForm 控件开发 (六) 为属性提供弹出式编辑对话框
    Dot Net WinForm 控件开发 (一) 写一个最简单的控件
    Dot Net WinForm 控件开发 (四) 设置属性的默认值
    Dot Net WinForm 控件开发 (二) 给控件来点描述信息
    Dot Net WinForm 控件开发 (八) 调试控件的设计时行为
    Dot Net WinForm 控件开发 (五) 复杂属性的子属性
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10959532.html
Copyright © 2011-2022 走看看