bzoj4541: [Hnoi2016]矿区

题目链接

bzoj4541

题目描述

Description

  平面上的矿区划分成了若干个开发区域。简单地说,你可以将矿区看成一张连通的平面图,平面图划分为了若干平面块,每个平面块即为一个开发区域,平面块之间的边界必定由若干整点(坐标值为整数的点)和连接这些整点的线段组成。每个开发区域的矿量与该开发区域的面积有关:具体而言,面积为s的开发区域的矿量为 s^2。现在有 m 个开采计划。每个开采计划都指定了一个由若干开发区域组成的多边形,一个开采计划的优先度被规定为矿量的总和÷开发区域的面积和;例如,若某开采计划指定两个开发区域,面积分别为 a和b,则优先度为(a^2+b^2)/(a+b)。由于平面图是按照划分开发区域边界的点和边给出的,因此每个开采计划也只说明了其指定多边形的边界,并未详细指明是哪些开发区域(但很明显,只要给出了多边形的边界就可以求出是些开发区域)。你的任务是求出每个开采计划的优先度。为了避免精度问题,你的答案必须按照分数的格式输出,即求出分子和分母,且必须是最简形式(分子和分母都为整数,而且都消除了最大公约数;例如,若矿量总和是 1.5,面积和是2,那么分子应为3,分母应为4;又如,若矿量和是 2,面积和是 4,那么分子应为 1,分母应为 2)。由于某些原因,你必须依次对每个开采计划求解(即下一个开采计划会按一定格式加密,加密的方式与上一个开采计划的答案有关)。具体的加密方式见输入格式。

Input

  第一行三个正整数 n,m,k,分别描述平面图中的点和边,以及开采计划的个数。接下来n行,第 i行(i=1,2,…,n)有两个整数x_i, y_i, 表示点i的坐标为(x_i, y_i)。接下来m行,第 i行有两个正整数a,b,表示点a和b 之间有一条边。接下来一行若干个整数,依次描述每个开采计划。每个开采计划的第一个数c指出该开采计划由开发区域组成的多边形边界上的点的个数为d=(c+P) mod n + 1;接下来d个整数,按逆时针方向描述边界上的每一个点:设其中第i个数为z_i,则第i个点的编号为(z_i+P) mod n + 1。其中P 是上一个开采计划的答案中分子的值;对于第 1 个开采计划,P=0。

Output

  对于每个开采计划,输出一行两个正整数,分别描述分子和分母。

Sample Input

9 14 5
0 0
1 0
2 0
0 1
1 1
2 1
0 2
1 2
2 2
1 2
2 3
5 6
7 8
8 9
1 4
4 7
5 8
3 6
6 9
4 8
1 5
2 6
6 8
3 3 0 4 7 1 3 4 6 4 8 0 4 3 6 2 3 8 0 4 6 2 5 0 4 5 7 6 3

Sample Output

1 1
1 2
1 1
9 10
3 4

HINT

输入文件给出的9个点和14条边描述的平面图如下所示:
bzoj4541: [Hnoi2016]矿区_第1张图片
第一个开采计划,输入的第1个值为3,所以该开采计划对应的多边形有(3+0) mod 8 +1=4个点,将接下的4个数3,0,4,7,分别代入(z_i+0) mod n + 1得到4个点的编号为4,1,5,8。计算出第一个开采计划的分子为1,分母为1。
类似地,可计算出余下开采计划的多边形的点数和点的编号:
第二个开采计划对应的多边形有3个点,编号分别为5, 6, 8。
第三个开采计划对应的多边形有6个点,编号分别为1, 2, 6, 5, 8, 4。
第四个开采计划对应的多边形有5个点,编号分别为1, 2, 6, 8, 4。
第五个开采计划对应的多边形有6个点,编号分别为1, 5, 6, 8, 7, 4。
对于100%的数据,n, k ≤ 2×10^5, m ≤ 3n-6, |x_ i|,|y_i| ≤ 3×10^4。所有开采计划的d之和不超过2×10^6。保证任何开采计划都包含至少一个开发区域,且这些开发区域构成一个连通块。保证所有开发区域的矿量和不超过 2^63-1。保证平面图中没有多余的点和边。保证数据合法。由于输入数据量较大,建议使用读入优化。

题解

考虑求出平面图的对偶图,平面图中的一个区域在对偶图中就是一个点。我们得到对偶图的dfs树。对于一个开采计划,一定包括了对偶图中的若干条边,并且这些边圈出了对偶图中的一个点集,这些点就是需要开采的点。我们记录一下dfs树的子树点权和,那么对于询问的一条树边我们判断它是进入子树还是离开子树,对应的答案加上或减去子树的权值和。对于非树边直接忽略就好了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;

char BUF[2000001],*buf,*end;
#define getch() (buf==end?fread(BUF,1,2000000,stdin),buf=BUF,end=buf+2000000,*(buf++):*(buf++))
inline void Read(int &x){
    static char c;
    int f=1;
    for(c=getchar();c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;
    for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
    x=x*f;
}

const int N=200010;
typedef long long ll;
struct point{
    int x,y;
    friend ll operator *(const point &a,const point &b){
        return (ll)a.x*b.y-(ll)b.x*a.y;
    }
    friend point operator -(const point &a,const point &b){
        point tmp;
        tmp.x=a.x-b.x; tmp.y=a.y-b.y;
        return tmp;
    }
}p[N];
struct edge{
    int u,v,id;
    double ang;
    edge(){}
    edge(int a,int b,int k){
        u=a; v=b; id=k;
        ang=atan2((double)p[b].y-p[a].y,(double)p[b].x-p[a].x);
    }
    friend bool operator <(const edge &a,const edge &b){
        return a.ang<b.ang;
    }
}e[6*N];
vector<edge> E[N],TE[N*2];
int Nex[N*6],v[N*6],cnt=1,num,rt;
int vi[N*2],fa[N*2],flag[N*6];
int n,m,k,x,y,rec,q[N];
ll ans1,ans2,P,S[N*2],S1[N*2];

int Find(int x,const edge &a){
    int mid,l=0,r=E[x].size();
    while(r-l>1){
        mid=(l+r)>>1;
        if(a<E[x][mid]) r=mid;
        else l=mid;
    }
    return l;
}
ll gcd(ll a,ll b){
    if(b==0) return a;
    return gcd(b,a%b);
}
void solve(){
    int now,tmp,st; ll s;
    for(int i=2;i<=cnt;i++)
    if(!v[i]){
        s=0; now=i; v[i]=++num; st=e[i].u;
        while(1){
            tmp=Nex[now]; v[tmp]=num;
            if(e[tmp].v==st) break;
            s+=(p[e[tmp].u]-p[st])*(p[e[tmp].v]-p[st]);
            now=tmp;
        }
        S[num]=s;
        if(s<=0) rt=num;
    }
    for(int i=2;i<=cnt;i++) 
    TE[v[i]].push_back(edge(v[i],v[i^1],i));
}
void dfs(int x){
    vi[x]=1; S1[x]=S[x]*S[x]; S[x]*=2;
    for(int i=0;i<(int)TE[x].size();i++)
    if(!vi[TE[x][i].v]){
        fa[TE[x][i].v]=x;
        flag[TE[x][i].id]=1;
        flag[TE[x][i].id^1]=1;
        dfs(TE[x][i].v);
        S[x]+=S[TE[x][i].v];
        S1[x]+=S1[TE[x][i].v];
    }
}
int main(){
    Read(n); Read(m); Read(k);
    for(int i=1;i<=n;i++) Read(p[i].x),Read(p[i].y);
    for(int i=1;i<=m;i++) {
        Read(x); Read(y);
        ++cnt; e[cnt]=edge(x,y,cnt);
        E[x].push_back(e[cnt]);
        ++cnt; e[cnt]=edge(y,x,cnt);
        E[y].push_back(e[cnt]);
    }
    for(int i=1;i<=n;i++) sort(E[i].begin(),E[i].end());
    for(int i=2;i<=cnt;i++){
        Nex[i]=Find(e[i].v,e[i^1])-1;
        if(Nex[i]<0) Nex[i]=E[e[i].v].size()-1;
        Nex[i]=E[e[i].v][Nex[i]].id;
    }
    solve(); dfs(rt);
    for(int i=1;i<=k;i++){
        Read(q[0]); q[0]=(q[0]+P)%n+1;
        for(int j=1;j<=q[0];j++) Read(q[j]),q[j]=(q[j]+P)%n+1;
        ans1=ans2=0; q[++q[0]]=q[1];
        for(int j=1;j<q[0];j++){
            x=q[j]; y=q[j+1];
            int tmp=Find(x,edge(x,y,0));
            tmp=E[x][tmp].id;
            if(!flag[tmp]) continue;
            if(v[tmp]==fa[v[tmp^1]]) ans1+=S[v[tmp^1]],ans2+=S1[v[tmp^1]];
            else ans1-=S[v[tmp]],ans2-=S1[v[tmp]];
        }
        if(ans1<0) ans1=-ans1,ans2=-ans2;
        ll d=gcd(ans1,ans2); ans1/=d; ans2/=d;
        printf("%lld %lld\n",P=ans2,ans1);
    }
    return 0;
}

你可能感兴趣的:(bzoj4541: [Hnoi2016]矿区)