Codeforces 223E. Planar Graph(平面图)

http://codeforces.com/problemset/problem/223/E

题解:

本题做法:
因为是个连通图,可以神奇的转换:
1.有一汇点T,每个点有一个流量朝汇点流,可以发现每个点的出流-入流=1(自己多的1)
2.从汇点T开始dfs,随便保留一个生成树作为流量树,发现一条边流量等于子树点数
3.对于一个环,它的内部点数=出去外面的流量-从外面来的流量
4.对环上每个点,把出边按几角排好序,预处理前缀和,在上面二分即可得到答案。

更加通用的做法:
1.平面图转对偶图
2.平面图欧拉公式:V+F=E+2
2.一个平面图环内的点数,=对偶图上删掉对应的边,环围成的点所在联通块的边数+2-点数。
3.相当于每条边有一个存在时间,查询一个时间一个点的联通块的边数和点数,经典分治+可撤销并查集即可解决。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 3e5 + 5;

#define db double

int n, m, x, y;

vector<int> e[N];
#define pb push_back
#define si size()

struct P {
	db x, y;
	P(db _x = 0, db _y = 0) {
		x = _x, y = _y;
	}	
} a[N];

P operator + (P a, P b) { return P(a.x + b.x, a.y + b.y);}
P operator - (P a, P b) { return P(a.x - b.x, a.y - b.y);}
db operator ^ (P a, P b) { return a.x * b.y - a.y * b.x;}
db operator * (P a, P b) { return a.x * b.x + a.y * b.y;}
P operator * (P a, db b) { return P(a.x * b, a.y * b);}

int fa[N], bz[N], siz[N];

db ang(P a, P b) {
	return atan2(b.y - a.y, b.x - a.x);
}

vector<P> s[N];

int cmpe(P a, P b) {
	return a.x < b.x;
}

int d[N], d0;

void dg(int x) {
	d[d0 = 1] = x; bz[x] = 1;
	for(int i = 1; i <= d0; i ++) {
		int x = d[i];
		ff(_y, 0, e[x].si) {
			int y = e[x][_y];
			if(bz[y]) continue;
			fa[y] = x;
			bz[y] = 1;
			d[++ d0] = y;
		}
	}
	fd(i, d0, 1) {
		int x = d[i];
		siz[x] = 1;
		ff(_y, 0, e[x].si) {
			int y = e[x][_y];
			if(fa[y] != x) continue;
			siz[x] += siz[y];
			s[x].pb(P(ang(a[x], a[y]), -siz[y]));
		}
		if(fa[x] <= n) {
			s[x].pb(P(ang(a[x], a[fa[x]]), siz[x]));
		}
		if(s[x].si) {
			sort(s[x].begin(), s[x].end(), cmpe);
			ff(j, 1, s[x].si) s[x][j].y += s[x][j - 1].y;
		}
	}
}

void build() {
	int t = 1;
	fo(i, 2, n) if(a[i].x < a[t].x) t = i;
	e[n + 1].pb(t); e[t].pb(n + 1);
	dg(n + 1);
}

int Q, k;
P b[N]; int c[N];

void check_order() {
	db s = 0;
	fo(i, 2, k - 1) s += (b[i] - b[1]) ^ (b[i + 1] - b[1]);
	if(s < 0) {
		reverse(b + 1, b + k + 1);
		reverse(c + 1, c + k + 1);
	}
}

const db pi = acos(-1);

const db eps = 1e-11;

ll ef(vector<P> &s, db p) {
	int as = -1;
	for(int l = 0, r = s.si - 1; l <= r; ) {
		int m = l + r >> 1;
		if(s[m].x <= p) as = m, l = m + 1; else r = m - 1;
	}
	if(as == -1) return 0;
	return s[as].y;
}

ll solve(int x, db p, db q) {
	ll ans = 0;
	if(fa[x] > n) ans += siz[x];
	if(s[x].si == 0) return ans;
	if(p <= q) ans += ef(s[x], p - eps) + (s[x][s[x].si - 1].y - ef(s[x], q + eps)); else
		ans += ef(s[x], p - eps) - ef(s[x], q + eps);
	return ans;
}

int main() {
	freopen("graph.in", "r", stdin);
	freopen("graph.out", "w", stdout);
	scanf("%d %d", &n, &m);
	fo(i, 1, m) {
		scanf("%d %d", &x, &y);
		e[x].pb(y); e[y].pb(x);
	}
	fo(i, 1, n) scanf("%lf %lf", &a[i].x, &a[i].y);
	build();
	scanf("%d", &Q);
	fo(ii, 1, Q) {
		scanf("%d", &k);
		fo(i, 1, k) {
			scanf("%d", &c[i]);
			b[i] = a[c[i]];
		}
		check_order();
		ll ans = 0;
		fo(i, 1, k) {
			ans += solve(c[i], ang(b[i], i == k ? b[1] : b[i + 1]), ang(b[i], i == 1 ? b[k] : b[i - 1]));
		}
		pp("%lld\n", ans);
	}
}
posted @ 2020-06-02 19:45  Cold_Chair  阅读(380)  评论(0编辑  收藏  举报