您的位置:首页 > 其它

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: