BZOJ 4541: [Hnoi2016]礦區 平面圖轉對偶圖+DFS樹
4541: [Hnoi2016]礦區
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 433 Solved: 182
[Submit][Status][Discuss]
Description
平面上的礦區劃分成了若幹個開發區域。簡單地說,你可以將礦區看成一張連通的平面圖,平面圖劃分為了若
幹平面塊,每個平面塊即為一個開發區域,平面塊之間的邊界必定由若幹整點(坐標值為整數的點)和連接這些整點
的線段組成。每個開發區域的礦量與該開發區域的面積有關:具體而言,面積為s的開發區域的礦量為 s^2。現在
有 m 個開采計劃。每個開采計劃都指定了一個由若幹開發區域組成的多邊形,一個開采計劃的優先度被規定為礦
/(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個整數,按逆時針方向描述邊界上的每一個點:
第 1 個開采計劃,P=0。
Output
對於每個開采計劃,輸出一行兩個正整數,分別描述分子和分母。
Sample Input
9 14 50 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 11 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。保證平面圖中沒有多余的點和邊。保證數據合
法。由於輸入數據量較大,建議使用讀入優化。
Source
題意:一個開采計劃優先度為$\frac{\sum S^2(X)}{\sum S(X)}$其中X∈A,S(x)為開發區域的面積。
想法:將原平面圖G轉對偶圖G‘後,一個域被算入的條件就是不與外面的無窮域Rt連通。如果以無窮域為根,DFS遍歷得到的樹T。對於在G‘中與Rt相連的要麽是Rt在T中的兒子,要麽是T中葉子節點。理由:DFS樹沒有橫叉邊。同時因為這個理由,一個開采計劃就一定是樹上一個連通塊。所以只要保證這個連通塊被割下來就好了咯。
具體過程:
1.平面圖G轉對偶圖G’。(不會戳這裏)
2.DFS遍歷圖G‘得到樹T,每個節點維護$S(x)$,$S^2(x)$子樹和
3.回答詢問。對於有向邊(u->v),先找到其在G‘中對應的上下域(a,b)。如果(a,b)是非樹邊,那麽就可以忽略,畢竟不會影響答案統計。是樹邊,就要分情況累加答案。
先分析一下:假設(u->v)方向的都割掉連通塊與葉子節點通路上的邊,那麽就肯定是減去b的子樹和,反之加上a的子樹和。如果(u->v)的不是,答案便要取反。所以這樣統計是可行的。判斷條件:a是不是b的父親。
總復雜度$O(M\log M+\sum d)$
#include< algorithm> #include< cmath > #include< cstdio > #include< vector > #define gec getchar #define FILE(F) freopen(F".in","r",stdin),freopen(F".out","w",stdout) #define DEBUG fprintf(stderr,"Passing [%s] in Line (%d)\n",__FUNCTION__,__LINE__); typedef long long ll; template inline void read(T&x) { x=0;bool f=0;char c=gec(); for(;c<‘0‘||c>‘9‘;c=gec())f=(c==‘-‘); for(;c>=‘0‘&&c<=‘9‘;c=gec())x=x*10+c-‘0‘; x=f?-x:x; } const int MAXN(200010),MAXM(600010); int n,m,k,a,b;ll P,Q; struct Coor { int x,y; Coor(){}; Coor(int a,int b):x(a),y(b){}; inline Coor operator +(const Coor&A){return Coor(x+A.x,y+A.y);} inline Coor operator -(const Coor&A){return Coor(x-A.x,y-A.y);} inline Coor operator *(const double k){return Coor(k*x,k*y);} inline Coor operator /(const double k){return Coor(x/k,y/k);} }PA[MAXN]; ll acorss(Coor A,Coor B) {return (ll)A.x*B.y-(ll)A.y*B.x;} ll dot(Coor A,Coor B) {return (ll)A.x*B.x+(ll)A.y*B.y;} //===================================================計算幾何 struct Next { int u,v,id;double ang; Next(){} Next(int a,int b,int k) {u=a; v=b; id=k; ang=atan2(PA[u].x-PA[v].x,PA[u].y-PA[v].y);} inline bool operator <(const Next &A)const {return ang<A.ang;} }e[MAXM<<1]; std::vectorEdge[MAXN]; int cnt=1,Nex[MAXM<<1],col[MAXM<<1];bool flag[MAXM<<1]; int etot,rt;ll S[MAXN<<1],Sp[MAXN<<1]; struct Node{int nd,nx;}bot[MAXM<<1]; int tot=1,first[MAXN<<1],fa[MAXN<<1];bool vis[MAXN<<1];//F-E+V=2 void add(int a,int b) {bot[++tot]=(Node){b,first[a]};first[a]=tot;} namespace planar_graph { int Two_Find(int num,Next &x) { int l=0,r=Edge[num].size()-1,mid,Ans; for(;l<=r;)if(x<Edge[num][mid=(l+r)>>1])r=mid-1;else l=mid+1,Ans=mid; return Ans; } void Get_planar() { for(int i=2;i<=cnt;i++) if(!col[i]) { int now=i;col[i]=++etot;ll tmp_s=0; for(;;) { now=Nex[now];col[now]=etot; if(e[now].v==e[i].u)break;//成環 tmp_s+=acorss(PA[e[now].v]-PA[e[i].u],PA[e[now].u]-PA[e[i].u]); } S[etot]=tmp_s; if(tmp_s<=0)rt=etot;//無窮域 } for(int i=2;i<=cnt;i++) add(col[i],col[i^1]);//相鄰域連邊 } void build() { for(int i=1;i<=m;i++) { read(a);read(b); ++cnt;e[cnt]=Next(a,b,cnt); Edge[a].push_back(e[cnt]); ++cnt;e[cnt]=Next(b,a,cnt); Edge[b].push_back(e[cnt]); } for(int i=1;i<=n;i++) std::sort(Edge[i].begin(),Edge[i].end()); for(int i=2;i<=cnt;i++) { Nex[i]=Two_Find(e[i].v,e[i^1])-1; if(Nex[i]<0)Nex[i]=Edge[e[i].v].size()-1; Nex[i]=Edge[e[i].v][Nex[i]].id; }//找下一條邊 Get_planar(); } } //===================================================平面圖轉對偶圖 namespace WW_HASH { const int MP(980321),HM(233333); int nx[MAXM<<1],head[MP]; void build_HASH() { for(int i=2,v;i<=cnt;i++) { v=((ll)e[i].u*HM%MP+e[i].v)%MP; nx[i]=head[v];head[v]=i; } } int Find(int a,int b) { int v=((ll)a*HM%MP+b)%MP; for(v=head[v];v;v=nx[v]) if(e[v].u==a&&e[v].v==b)return v; exit(0);//不存在的 } } void Dfs(int x) { vis[x]=1; Sp[x]=S[x]*S[x]; S[x]<<=1; for(int v=first[x];v;v=bot[v].nx) if(!vis[bot[v].nd]) { fa[bot[v].nd]=x; flag[v]=flag[v^1]=1; Dfs(bot[v].nd); S[x]+=S[bot[v].nd]; Sp[x]+=Sp[bot[v].nd]; } } ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} const int SUMD(2000010); int d,z[SUMD]; int main() { #ifndef ONLINE_JUDGE FILE("C"); #endif read(n);read(m);read(k); for(int i=1;i<=n;i++) read(PA[i].x),read(PA[i].y); planar_graph::build(); WW_HASH::build_HASH(); Dfs(rt); // DEBUG; for(int i=1;i<=k;i++) { read(d);d=(d+P)%n +1; for(int j=1;j<=d;j++) read(z[j]),z[j]=(z[j]+P)%n +1; z[0]=z[d]; P=Q=0; for(int j=1,id;j<=d;j++) { id=WW_HASH::Find(z[j-1],z[j]); // fprintf(stderr,"id:%d\n",id); if(!flag[id])continue;//非樹邊 if(fa[col[id]]==col[id^1]) P-=Sp[col[id]],Q-=S[col[id]]; else P+=Sp[col[id^1]],Q+=S[col[id^1]]; // fprintf(stderr,"%lld %lld\n",P,Q); } if(P<0)P=-P,Q=-Q; // DEBUG; // fprintf(stderr,"%lld %lld\n",P,Q); ll g=gcd(P,Q);P/=g;Q/=g; printf("%lld %lld\n",P,Q); } //DEBUG; return 0; }
BZOJ 4541: [Hnoi2016]礦區 平面圖轉對偶圖+DFS樹