題目
Description
給定一張無重邊、自環、割點的平面圖,你需要回答 Q 次詢問,每次詢問會給出一個簡單環,你需要回答在由這個簡單環圍成的多邊形內部(包括邊界上)的點有多少個。
保證圖中每條線段不會通過除這條線段端點以外的其他點。
請仔細閱讀題目讀入輸出的格式。
Input
第一行兩個正整數 n, m 表示平面圖有 n 個點和 m 條邊。
接下來 m 行,每行兩個正整數 a, b 表示平面圖上的一條邊 (a, b)。
接下來 n 行,每行兩個整數 xi , yi 表示第 i 個點的座標。
接下來一行一個整數 Q 表示有 Q 組詢問。
接下來 Q 行每行表示一個詢問。
每一行第一個整數 K 表示詢問的環的大小,
接下來 K 個整數,按照順時針或者逆時針的順序給出環上的點。
保證讀入數據一定有至少一個對應的 AC 自動機。
Output
Q 行,每行輸出一個整數表示第 i 個詢問的答案。
Sample Input
5 8
3 4
5 2
5 1
1 4
2 4
5 3
1 3
2 3
1 5
4 -5
4 -3
5 2
-5 -2
4
4 5 2 3 1
3 3 1 4
3 5 2 3
4 2 3 1 4
Sample Output
4
3
3
4
Data Constraint
思路
考慮一個模型:每個點有一個單位的流量從這個點流出,流量沿着邊傳送,最後所有點流出的流量彙集到某個點。
那麼每條邊在某個方向上可能有一個流量,除了匯點其他所有點的流出流量-流入流量=1。
一個環當中的點的個數就是流出環的流量總和減去流入環的流量總和。
爲了避免出現詢問最外面的環的情況(在絕大多數數據中我加了這種情況),我們在這個圖的最外面加一個點並向圖
連邊,以這個新加的點作爲匯點。標程的做法是在x座標最小的點左邊連接這個點。
構造這個流量可以在圖上隨便搞一個以匯點爲根的生成樹,每個點往父親的流量大小是這個點的子樹大小。
注意到環的總長不超過3e5 ,所以每次繞環走一圈是可以做到的。
代碼
#include <bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
const int N = 1e5 + 10;
int n, m;
struct P{
int x,y;
P(int _x=0, int _y=0) {x=_x,y=_y;}
P operator-(P b){return P(x-b.x,y-b.y);}
P operator+(P b){return P(x+b.x,y+b.y);}
ll operator*(P b){return (ll)x*b.y-(ll)y*b.x;}
P operator*(ll r){return P(x*r,y*r);}
} p[N];
vector<pair<db,int> > e[N];
vector<int>sum[N];
map<int,int> loc[N];
int sz[N],vis[N];
int fa[N];
void bfs() {
static int Q[N], h = 0, t = 0;
Q[++t] = n + 1;
vis[n+1] = 1;
while(h < t){
int x = Q[++h];
for(auto y:e[x]){
if(!vis[y.second]){
Q[++t]=y.second;
fa[y.second] = x;
vis[y.second]=1;
}
}
}
assert(t == n + 1);
for(int i = n + 1; i; i--) {
int x = Q[i];
if(x == n + 1) continue;
sz[x]++;
sz[fa[x]] += sz[x];
sum[x].resize(e[x].size());
for(auto y:e[x]){
if(fa[y.second] == x){
sum[x][loc[x][y.second]]=-sz[y.second];
}
}
sum[x][loc[x][fa[x]]]=sz[x];
for(int i=1; i<sum[x].size(); i++)
sum[x][i]+=sum[x][i-1];
}
}
int t[N];
void wr(P a){
printf("%d %d\n",a.x,a.y);
}
void wr(vector<int> a){
for(int x:a)printf("%d ",x);
printf("\n");
}
int main() {
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
cin >> n >> m;
int mix=1e9;
for(int i = 1; i <= m; i++) {
int x,y;scanf("%d %d",&x,&y);
e[x].push_back(make_pair(0,y));
e[y].push_back(make_pair(0,x));
}
p[n+1]=P(-1e9,-1e9);
for(int i = 1; i <= n; i++) {
scanf("%d %d",&p[i].x,&p[i].y);
mix=min(mix,p[i].x);
}
for(int i = 1; i <= n; i++) if(p[i].x == mix) {
e[i].push_back(make_pair(0,n+1));
e[n+1].push_back(make_pair(0,i));
break;
}
for(int i = 1; i <= n + 1; i++) {
for(auto &y:e[i]){
y.first = atan2(p[y.second].y - p[i].y, p[y.second].x - p[i].x);
}
sort(e[i].begin(), e[i].end());
for(int j = 0; j < e[i].size(); j++) {
loc[i][e[i][j].second] = j;
}
}
bfs();
int q = 0; cin >> q;
for(int o = 1; o <= q; o++) {
int k; scanf("%d", &k);
int ans = 0;
for(int i = 1; i <= k; i++) scanf("%d",&t[i]);
ll u=0;
for(int i=2; i<k; i++)
u+=(p[t[i]]-p[t[1]])*(p[t[i+1]]-p[t[1]]);
if(u<0)reverse(t+1,t+1+k);
t[0]=t[k],t[k+1]=t[1];
for(int i=1; i<=k; i++){
int l=loc[t[i]][t[i-1]],r=loc[t[i]][t[i+1]];
if(l<=r){
assert(l!=r);
ans+=sum[t[i]][r-1]-sum[t[i]][l];
}else{
ans+=sum[t[i]].back()-sum[t[i]][l];
if(r>0) ans+=sum[t[i]][r-1];
}
}
printf("%d\n",ans);
}
}