BZOJ 4541: [Hnoi2016]礦區
阿新 • • 發佈:2019-02-20
平面圖轉對偶圖完全不會啊,只好學一下了
Orz了清哥的程式碼,感覺思路很清晰啊
順手寫個學習筆記吧。
方便起見編號為i的邊與編號為i^1的邊互為反向邊。
對於每個點進行極角排序,同時用一個數組表示該邊的下一條邊。
於是乎我們從邊i出發,到i的反向邊的下一條邊,畫個圖可以發現這麼走形成的域一定在這些邊的右邊,並且他們之中僅包含一個域(因為是貼著右邊走的),這個時候可以用叉積算下面積,如果面積為負,那麼一定是貼著最外面走的,右邊是無窮域。
所以我們得到了每條有向邊右邊的域,每條有向邊在對偶圖中也對應著一條有向邊,我們不妨設對偶圖中的有向邊由i的右邊的域指向i的左邊的域。
然後對偶圖就建好了。
其實思路很簡單的
那麼考慮一下這道題,我們以無窮域為根節點搜尋出一棵dfs樹
那麼dfs樹上的邊在開發區域中肯定會進♂進♂出♂出,但是這樣進♂進♂出♂出沒有快感啊,怎麼辦呢
我們考慮求出dfs樹上每個節點子樹的權值和,那麼進♂去的時候加上子樹的權值和,出來的時候減去剩下子樹的權值和,於是就得到答案(有快感)了
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<cmath> using namespace std; int read(){char ch=getchar();int x=0,f=1;while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;} #define rep(i,l,r) for(int i=l;i<=r;i++) #define per(i,r,l) for(int i=r;i>=l;i--) const int N=200000+5; const int M=600000+5; typedef long long ll; ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} struct point{ll x,y;}p[N]; point operator - (point a,point b){return (point){a.x-b.x,a.y-b.y};} ll cross(point a,point b){return a.x*b.y-a.y*b.x;} ll cross(point a,point b,point c){return cross(b-a,c-a);} struct Edge{int to,next;}e[M<<1]; int head[N<<2]; struct edge{ int u,v,ri; double slop; edge(){} edge(int _u,int _v):u(_u),v(_v),ri(0){ slop=atan2(p[v].y-p[u].y,p[v].x-p[u].x); } }o[M<<1]; void ins(int i){e[i]=(Edge){o[i^1].ri,head[o[i].ri]};head[o[i].ri]=i;} vector<int>g[N]; bool cmp(int i,int j){return o[i].slop<o[j].slop;} bool pmc(int i,int j){return o[i].v<o[j].v;} int next[M<<1],q[M<<1]; ll area[N<<2],sum[N<<2][2]; int dual,root,fa[N<<2]; bool vis[N<<2],tree[M<<1]; void dfs(int u){ vis[u]=true; if(u^root)sum[u][0]=area[u]*area[u],sum[u][1]=area[u]*2; for(int i=head[u];i;i=e[i].next){ int v=e[i].to;if(vis[v])continue; fa[v]=u;tree[i]=tree[i^1]=true;dfs(v); sum[u][0]+=sum[v][0];sum[u][1]+=sum[v][1]; } } int find(int u,int v){ int l=0,r=g[u].size()-1; while(l<=r){ int mid=(l+r)/2; if(v<=o[g[u][mid]].v)r=mid-1; else l=mid+1; } return g[u][r+1]; } int main(){ //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); int n,m,k;n=read();m=read();k=read(); rep(i,1,n)p[i].x=read(),p[i].y=read(); rep(i,1,m){ int u,v;u=read();v=read(); o[i<<1]=edge(u,v);o[i<<1|1]=edge(v,u); g[u].push_back(i<<1);g[v].push_back(i<<1|1); } rep(i,1,n){ sort(g[i].begin(),g[i].end(),cmp); for(int j=0,k=g[i].size();j<k;j++) next[g[i][j]]=g[i][(j+1)%k]; sort(g[i].begin(),g[i].end(),pmc); } rep(i,2,(m<<1|1)) if(!o[i].ri){ int j=i;dual++;q[0]=0; do o[j].ri=dual,q[++q[0]]=o[j].v,j=next[j^1];while(j^i); rep(j,2,q[0]-1) area[dual]+=cross(p[q[1]],p[q[j+1]],p[q[j]]); if(area[dual]<0)root=dual; } rep(i,2,(m<<1|1))ins(i); dfs(root); ll ans1=0,ans2=0; while(k--){ q[0]=read();q[0]=(q[0]+ans1)%n+1; rep(i,1,q[0])q[i]=read(),q[i]=(q[i]+ans1)%n+1; ans1=0;ans2=0; rep(i,1,q[0]){ int u=q[i],v=q[i%q[0]+1],k=find(u,v); if(tree[k]){ if(fa[o[k].ri]==o[k^1].ri) ans1-=sum[o[k].ri][0],ans2-=sum[o[k].ri][1]; else ans1+=sum[o[k^1].ri][0],ans2+=sum[o[k^1].ri][1]; } } ll d=gcd(ans1,ans2);ans1/=d,ans2/=d; printf("%lld %lld\n",ans1,ans2); } return 0; }