zoukankan      html  css  js  c++  java
  • bzoj4541 [Hnoi2016]矿区

    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条边描述的平面图如下所示:

    第一个开采计划,输入的第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。保证平面图中没有多余的点和边。保证数据合法。由于输入数据量较大,建议使用读入优化。

    正解:平面图转对偶图+生成树+$hash$。

    终于把$HNOI2016$做完了。。

    感觉这题也不是那么不可做?虽然我还是什么都不会。。

    首先我们考虑把这个平面图转成对偶图,怎样把平面图转成对偶图呢?

    把所有无向边拆成两条,然后对于每个点,把以它为起点的边极角排序一下。

    每次从一条还不属于任意一个域的边开始找,找到这条边的终点后,终点变成起点,找在这个点上这条反边的上一个极角序的边。

    然后重复上一个操作,直到回到最初的起点,这就是平面图的一个域了。

    我们把域和域之间连边,以无界域为根结点做一棵生成树,统计子树面积和和子树的面积平方和。

    如何找到无界域?我们要求每一个域的面积,这个用向量叉积求比较方便。如果这个域的有向面积$<=0$,那么它就是无界域。

    然后我们询问的时候,找出每一条边,这个用$hash$就能快速找到了。

    首先,如果这条边是非树边,那么我们可以直接忽略掉。否则对于每一条边,我们考虑它的域是它反边所在域的儿子还是父亲。

    如果是儿子,那么加上它所在的域的权值和,否则减去它反边所在域的权值和。

    证明不太会。。画一下图感性理解下就好。。

      1 #include <bits/stdc++.h>
      2 #define il inline
      3 #define RG register
      4 #define ll long long
      5 #define M (1200010)
      6 #define N (200010)
      7 #define rhl (2341687)
      8 
      9 using namespace std;
     10 
     11 struct point{
     12   int x,y;
     13   il point operator - (const point &a) const{
     14     return (point){x-a.x,y-a.y};
     15   }
     16 }p[N];
     17 
     18 struct edge{
     19   int u,v,id; double ang;
     20   il bool operator < (const edge &a) const{
     21     return ang<a.ang;
     22   }
     23 }g[M];
     24 
     25 struct E{ int nt,to,id; }G[M];
     26 struct H{ int nt,x,y,id; }hsh[M];
     27 
     28 vector <edge> e[N];
     29 
     30 int hd[rhl+10],head[M],vis[M],fa[M],in[M],bl[M],nxt[M],ask[M<<1],n,m,k,rt,num,cnt,ecnt,hcnt;
     31 ll sp[M],s[M],P,Q;
     32 
     33 il int gi(){
     34   RG int x=0,q=1; RG char ch=getchar();
     35   while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
     36   if (ch=='-') q=-1,ch=getchar();
     37   while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
     38   return q*x;
     39 }
     40 
     41 il ll gcd(RG ll a,RG ll b){ return b ? gcd(b,a%b) : a; }
     42 
     43 il ll cross(RG point a,RG point b){
     44   return 1LL*a.x*b.y-1LL*a.y*b.x;
     45 }
     46 
     47 il void insert(RG int a,RG int b){
     48   RG double ang=atan2(p[b].y-p[a].y,p[b].x-p[a].x);
     49   ++num,g[num]=(edge){a,b,num,ang},e[a].push_back(g[num]); return;
     50 }
     51 
     52 il void Insert(RG int from,RG int to,RG int id){
     53   G[++ecnt]=(E){head[from],to,id},head[from]=ecnt; return;
     54 }
     55 
     56 il void ins(RG int from,RG int x,RG int y,RG int id){
     57   hsh[++hcnt]=(H){hd[from],x,y,id},hd[from]=hcnt; return;
     58 }
     59 
     60 il void add(RG int x,RG int y,RG int id){
     61   RG int wyh=(1LL*x*N+y)%rhl; ins(wyh,x,y,id); return;
     62 }
     63 
     64 il int get(RG int x,RG int y){
     65   RG int wyh=(1LL*x*N+y)%rhl;
     66   for (RG int i=hd[wyh];i;i=hsh[i].nt)
     67     if (hsh[i].x==x && hsh[i].y==y) return hsh[i].id;
     68   return 0;
     69 }
     70 
     71 il void dfs(RG int x){
     72   vis[x]=1,sp[x]=s[x]*s[x],s[x]<<=1; RG int v;
     73   for (RG int i=head[x];i;i=G[i].nt){
     74     v=G[i].to; if (vis[v]) continue;
     75     fa[v]=x,in[G[i].id]=in[G[i].id^1]=1;
     76     dfs(v),sp[x]+=sp[v],s[x]+=s[v];
     77   }
     78   return;
     79 }
     80 
     81 int main(){
     82 #ifndef ONLINE_JUDGE
     83   freopen("mine.in","r",stdin);
     84   freopen("mine.out","w",stdout);
     85 #endif
     86   n=gi(),m=gi(),k=gi(),num=1; RG int x,y;
     87   for (RG int i=1;i<=n;++i) x=gi(),y=gi(),p[i]=(point){x,y};
     88   for (RG int i=1;i<=m;++i) x=gi(),y=gi(),insert(x,y),insert(y,x);
     89   for (RG int i=1,sz;i<=n;++i){
     90     sort(e[i].begin(),e[i].end()),sz=e[i].size();
     91     for (RG int j=1;j<sz;++j) nxt[e[i][j].id]=e[i][j-1].id;
     92     nxt[e[i][0].id]=e[i][sz-1].id;
     93   }
     94   for (RG int i=2;i<=num;++i){
     95     if (bl[i]) continue; bl[i]=bl[nxt[i^1]]=++cnt;
     96     for (RG int now=nxt[i^1];g[now].v!=g[i].u;now=nxt[now^1],bl[now]=cnt)
     97       s[cnt]+=cross(p[g[now].u]-p[g[i].u],p[g[now].v]-p[g[i].u]);
     98     if (s[cnt]<=0) rt=cnt;
     99   }
    100   for (RG int i=2;i<=num;++i) Insert(bl[i],bl[i^1],i),add(g[i].u,g[i].v,g[i].id); dfs(rt);
    101   for (RG int q=1,d;q<=k;++q){
    102     d=(gi()+P)%n+1; for (RG int i=1;i<=d;++i) ask[i]=(gi()+P)%n+1;
    103     ask[d+1]=ask[1],P=Q=0;
    104     for (RG int i=1,now;i<=d;++i){
    105       now=get(ask[i],ask[i+1]); if (!in[now]) continue;
    106       if (fa[bl[now]]==bl[now^1]) P+=sp[bl[now]],Q+=s[bl[now]];
    107       else P-=sp[bl[now^1]],Q-=s[bl[now^1]];
    108     }
    109     RG ll gg=gcd(P,Q); P/=gg,Q/=gg; printf("%lld %lld
    ",P,Q);
    110   }
    111   return 0;
    112 }
  • 相关阅读:
    PasswordHasher 算法
    Java的环境变量配置
    CSS2+DIV的学习心得
    struts2利用token机制拦截用户重复提交
    电脑开机出现 error: no such partition grub rescue
    MySQL limit实际用法的详细解析
    win7将硬盘由ATA修改为AHCI的方法
    与SVN相关的程序的调试问题【转】
    【转】JSP提交表单
    LigerUI 快速开发UI框架 链接
  • 原文地址:https://www.cnblogs.com/wfj2048/p/7435491.html
Copyright © 2011-2022 走看看