bzoj4541 [Hnoi2016]矿区
Posted wfj_2048
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
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 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]矿区的主要内容,如果未能解决你的问题,请参考以下文章