• 「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;
    }
    
  • 相关阅读:
    MySQL3:索引
    MySQL4:存储过程和函数
    MySQL2:四种MySQL存储引擎
    MySQL1:MySQL函数汇总
    web.xml详解
    SharePoint 多行文本字段设置默认值
    SharePoint Online 自定义Modern UI表单
    Server-side activities have been updated. You need to restart SharePoint Designer to use the updated version of activities.
    计算请假天数JavaScript方法
    SharePoint 2019 离线安装准备工具
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10959532.html
Copyright © 2020-2023  润新知