1. 程式人生 > >bzoj4541 [Hnoi2016]礦區

bzoj4541 [Hnoi2016]礦區

cor 結點 多邊形 mes pre sort 它的 哪些 opera

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 }

bzoj4541 [Hnoi2016]礦區