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\n",P,Q);
110   }
111   return 0;
112 }

 

posted @ 2017-08-26 15:43  wfj_2048  阅读(220)  评论(0编辑  收藏  举报