HNOI2016 矿区
2016-04-20 15:19
148 查看
题目大意
给定一个N个点M条边的平面图G。有Q个询问,每次询问平面图上的一个区域A,(逆时针地给定多边形的点集),你需要求出∑P⊂AS(P)2∑P⊂AS(P),S(P)表示P这个面的面积。数据范围
N≤2∗105,M≤3N−6∑询问点数≤2∗106
题解
首先我们要构成G的对偶图,设为G′,以无穷域为根,构出G′的生成树T。以无穷域为根是有原因的,等下会讲。接下来,对于一个询问A,我们依次在T中将A中相邻点的连边在G′中对应的对偶边删掉(假如不在生成树中则不管),接下来T会裂成若干个联通块,假如一个联通块被我们选出来的边围住了,那么我们就可以将这个联通块的答案统计进去了。但怎么定义围住了?设我们当前要删掉的边为(u,v),注意边是有方向的,就是一个向量,那么假如在树中,u为v的父亲,那么我们就加上Subv的信息,若v为u的父亲,那么我们减去Subu的信息。那么我们最终就可以得到所有合法联通块的信息了。
这样为什么是对的?
1. 不会计多。假如一个面u不在A中,但最后却被统计入了某个合法的联通块中,那么这个联通块中必然会存在一条边(a,b)满足a在A中而b不在A中,但这样的边我们一开始就会删掉,因此矛盾。
2. 不会计少。注意到我们唯一计少的情况就是存在一个面u,使得u在A中,但u不在任一合法的联通块中。注意到一个联通块的边界必然是由A的边界面所构成,而u在A中,那么u在生成树中至少与一个A中的边界面联通(把他围住了),所以u必然在某个联通块中,矛盾。
因此,我们就证明了这个算法是正确的。以无穷域为根就是因为无穷域不可能在A中,那么就能保证每个在A中的面在生成树中必然存在祖先边,也就是与某个边界面联通。
那么这题就比较简单了,一开始先构出对偶图,以无穷域为根做生成树,然后对于询问扫一下每条边即可。
由于构出对偶图需要每个点对每条边按极角排序,所以总的复杂度为O(MlogM+∑询问点数)
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <vector> #include <map> #define fe first #define se second using namespace std; typedef unsigned long long LL; typedef pair<int,int> P; const int MAXN = 2000005,HAS_1 = 3999647,HAS_2 = 3983633; LL operator ^(const P &a,const P &b) {return LL(a.fe) * b.se - LL(a.se) * b.fe;} vector<int> Lk[MAXN],E[MAXN]; LL S[MAXN],Are[MAXN]; long long Ar[MAXN]; bool Walk[MAXN],In[MAXN]; double Ang[MAXN]; P Po[MAXN]; int To[MAXN],Bel[MAXN],Rank[MAXN],N,M,K,tot,cnt; int V[HAS_1][3]; void Push(LL a,int ref) { int l = a % HAS_1; for(;V[l][2];l = (l + 1) % HAS_1); V[l][0] = a % HAS_1,V[l][1] = a % HAS_2,V[l][2] = ref; } int Find(LL a) { int l = a % HAS_1,l1 = a % HAS_2,ll = a % HAS_1; for(;V[l][0] != ll || V[l][1] != l1;l = (l + 1) % HAS_1); return V[l][2]; } void Link(int u,int v) { To[++ tot] = v,Ang[tot] = atan2(Po[v].se - Po[u].se,Po[v].fe - Po[u].fe),Lk[u].push_back(tot); Push(u * 1ll * N + v,tot); } inline bool cmp(const int &a,const int &b) {return Ang[a] < Ang[b];} void Dfs(int Now,int Pr) { for(;!Walk[Now];) { Walk[Now] = 1; Bel[Now] = cnt; Ar[cnt] += (Po[To[Now]] ^ Po[Pr]); Pr = To[Now]; Now = Lk[To[Now]][(Rank[Now ^ 1] + 1) % Lk[To[Now]].size()]; } } void Dfs_T(int Now) { static int Q[MAXN]; Q[1] = Now; int fi = 1,en = 1; for(;fi <= en;fi ++) { Now = Q[fi]; Walk[Now] = 1; S[Now] = Are[Now] * Are[Now]; for(int i = 0;i < E[Now].size();i ++) { int v = E[Now][i]; if (Walk[Bel[v ^ 1]]) continue; Walk[Bel[v ^ 1]] = 1; In[v] = 1; Q[++ en] = Bel[v ^ 1]; } } for(;en;en --) { Now = Q[en]; for(int i = 0;i < E[Now].size();i ++) { int v = E[Now][i]; if (!In[v]) continue; S[Now] += S[Bel[v ^ 1]],Are[Now] += Are[Bel[v ^ 1]]; } } } LL Gcd(LL a,LL b) {return b ? Gcd(b,a % b) : a;} int Search(int u,int v) {return Find(u * 1ll * N + v);} template<class T> void read(T &x) { char c; bool f = 0; while (c = getchar(),(c < '0' || c > '9') && c != '-'); if (c == '-') f = 1,x = 0; else x = c - 48; while (c = getchar(),(c >= '0' && c <= '9')) x = x * 10 + c - 48; if (f) x = -x; } int main() { scanf("%d%d%d", &N, &M, &K); for(int i = 1;i <= N;i ++) read(Po[i].fe),read(Po[i].se); tot = 1; for(int i = 1;i <= M;i ++) { int u,v; read(u),read(v); Link(u,v),Link(v,u); } for(int i = 1;i <= N;i ++) stable_sort(Lk[i].begin(),Lk[i].end(),cmp); for(int i = 1;i <= N;i ++) for(int j = 0;j < Lk[i].size();j ++) Rank[Lk[i][j]] = j; for(int i = 1;i <= N;i ++) for(int j = 0;j < Lk[i].size();j ++) { int v = Lk[i][j]; if (Walk[v]) continue; ++ cnt; Dfs(v,i); } for(int i = 2;i <= tot;i ++) { int u = To[i],v = To[i ^ 1]; E[Bel[i]].push_back(i); } memset(Walk,0,sizeof Walk); int Root = 0; for(int i = 1;i <= cnt;i ++) if (Ar[i] < 0) Root = i,Ar[i] = 0; for(int i = 1;i <= cnt;i ++) Are[i] = Ar[i]; Dfs_T(Root); LL lst = 0; for(int i = 1;i <= K;i ++) { static long long Z[MAXN]; long long C = 0; read(C); C = (C + lst) % N + 1; for(int j = 1;j <= C;j ++) { read(Z[j]); Z[j] = (Z[j] + lst) % N + 1; } LL Anstop = 0,Ansbot = 0; bool ok = 0; for(int j = 1;j <= C;j ++) { int u = Z[j],v = Z[j % C + 1]; int ref = Search(u,v); if (!(In[ref] || In[ref ^ 1])) continue; if (In[ref]) ok = 1,Anstop += S[Bel[ref ^ 1]],Ansbot += Are[Bel[ref ^ 1]]; else Anstop -= S[Bel[ref]],Ansbot -= Are[Bel[ref]]; } Ansbot *= 2; LL gcd = Gcd(Anstop,Ansbot); Anstop /= gcd,Ansbot /= gcd; printf("%lld %lld\n", Anstop,Ansbot); lst = Anstop % N; } return 0; }
相关文章推荐
- Access denied for user 'mysql用户名'@'主机或IP' (using password: YES)'
- 查看CentOS服务器的cpu、内存、操作系统版本信息
- ubuntu14.04 64位系统下编译3.13.11内核源码
- 查看当前正在运行的Activity列表
- http://www.cnblogs.com/endv/p/5205435.html
- 自定义导航控件
- vimdiff
- STM8L051F3单片机竟然没有TIM1定时器,却有TIM2,3,4!!!害我调了一天没调出来
- ABAP中SE71修改Form之后如何生效
- 色度编码采样
- iOS之获取App的LaunchImage
- 数组做数据成员(1)
- 2. Add Two Numbers
- UI用到的一些表或视图
- hiho 40 三分·三分求极值
- android计时与系统休眠
- FIG草根时代计划
- dialog表单填写的常用效果功能实现
- Mysql(或者sqlite), Mongo中update Column + 1
- centos install zookeeper cluster