BZOJ4541 HNOI2016矿区(平面图转对偶图)

  考虑先将平面图转化为对偶图。具体地,将无向边拆成两条有向边。每次考虑找到包围一个区域的所有边。对当前考虑的边,找到该边的反向边在该边终点的出边集中,按极角序排序的后继,这条后继边也是包围该区域的边。这样对偶图就建好了。

  考虑怎么用对偶图解决原问题。将外围的无限域也作为对偶图中的一个点,以其为根随便找一棵生成树,计算子树内面积和及面积平方和。对于询问,考虑多边形上每条边,其同时也是对偶图中两点的边。如果该边在生成树中是非树边,扔掉不管;如果是树边,若由父亲指向儿子,则加上儿子权值,否则减掉儿子权值。具体只能感性理解。

  注意对偶图中两点间可能有重边,判断是否为非树边时小心一点。因为这个调一年也是没谁了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
#include<cassert>
using namespace std;
#define ll long long
#define Vector point
#define N 400010
#define M 1200010
ll gcd(ll n,ll m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,m,q,b[N],sur[M],nxt[M],t=-1;
map<int,int> id[N],tag;
ll lastx,lasty;
struct point
{
    int x,y;
    Vector operator +(const Vector&a) const
    {
        return (Vector){x+a.x,y+a.y};
    }
    Vector operator -(const Vector&a) const
    {
        return (Vector){x-a.x,y-a.y};
    }
    ll operator *(const Vector&a) const
    {
        return 1ll*x*a.y-1ll*y*a.x;
    }
    bool operator <(const Vector&a) const
    {
        return atan2(x,y)<atan2(a.x,a.y);
    }
}a[N];
struct edge{int x,y;}e[M];
struct data
{
    Vector p;int id;
    bool operator <(const data&a) const
    {
        return p<a.p;
    }
};
vector<data> c[N];
namespace newgraph
{
    int n,p[N],fa[N],t=0,root;
    ll sum[N],sqr[N],area[N];
    struct data{int to,nxt,id;}edge[M];
    void addedge(int x,int y,int z){t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].id=z,p[x]=t;}
    void dfs(int k)
    {
        sum[k]=area[k]<<1,sqr[k]=area[k]*area[k];
        for (int i=p[k];i;i=edge[i].nxt)
        if (!fa[edge[i].to]&&edge[i].to!=root)
        {
            tag[edge[i].id]=-1,tag[edge[i].id^1]=1;
            fa[edge[i].to]=k;
            dfs(edge[i].to);
            sum[k]+=sum[edge[i].to],sqr[k]+=sqr[edge[i].to];
        }
    }
}
void addedge(int x,int y)
{
    t++,e[t].x=x,e[t].y=y;id[x][y]=t;
    c[x].push_back((data){a[y]-a[x],t});
}
void build()
{
    for (int i=1;i<=n;i++)
    if (c[i].size())
    {
        sort(c[i].begin(),c[i].end());
        for (int j=0;j<c[i].size()-1;j++)
        nxt[c[i][j].id]=c[i][j+1].id;
        nxt[c[i][c[i].size()-1].id]=c[i][0].id;
    }
    for (int i=0;i<(m<<1);i++)
    if (!sur[i])
    {
        ++newgraph::n;
        int x=i;ll y=0;
        do
        {
            sur[x]=newgraph::n;
            y+=a[e[x].x]*a[e[x].y];
            x=nxt[x^1];
        }while (!sur[x]);
        if (y<0) newgraph::root=newgraph::n;
        newgraph::area[newgraph::n]=y;
    }
    for (int i=0;i<(m<<1);i++)
    newgraph::addedge(sur[i],sur[i^1],i);
    newgraph::dfs(newgraph::root);
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4541.in","r",stdin);
    freopen("bzoj4541.out","w",stdout);
    const char LL[]="%I64d ";
#else 
    const char LL[]="%lld ";
#endif
    n=read(),m=read(),q=read();
    for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read();
    for (int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        addedge(x,y),addedge(y,x);
    }
    build();
    while (q--)
    {
        int t=(read()+lastx)%n+1;
        for (int i=1;i<=t;i++) b[i]=(read()+lastx)%n+1;
        lastx=0,lasty=0;
        for (int i=1;i<=t;i++)
        {
            int x=tag[id[b[i]][b[i%t+1]]];
            if (x)
            {
                int y=x==1?sur[id[b[i]][b[i%t+1]]]:sur[id[b[i%t+1]][b[i]]];
                lasty+=x*newgraph::sum[y],lastx+=x*newgraph::sqr[y];
            }
        }
        ll tmp=gcd(lastx,lasty);
        lastx/=tmp,lasty/=tmp;
        printf(LL,lastx),printf(LL,lasty),printf("\n");
    }
    return 0;
}

 

posted @ 2019-01-13 01:27  Gloid  阅读(181)  评论(0编辑  收藏  举报