【MX-X1-T6】「KDOI-05」简单的图上问题
【MX-X1-T6】「KDOI-05」简单的图上问题
直接计算连通块数量难度较大,由平面图性质利用欧拉公式
与 std 不同,这里用同一种方法统计点、边、面的贡献。
对于统计每个面的贡献,建出平面图的对偶图,再以无界面的点为根随便建一颗生成树。脑补一下图:一棵树树枝插进一个环里。如何快速给环覆盖上的每个节点都加 1 呢?当树枝进入环时,即节点
如何统计点和边的贡献?回到原图,如果一个面被环包含,那么所有面上的点也被环包含。所以可以再最后把每个面的贡献加到点上,这样每个点
不用平衡树自然是最优解。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cassert>
#define PII pair<int,int>
#define x first
#define y second
typedef long long ll;
using namespace std;
PII operator-(const PII& u,const PII& v)
{
return {u.x-v.x,u.y-v.y};
}
ll operator*(const PII& u,const PII& v)
{
return 1LL*u.x*v.y-1LL*u.y*v.x;
}
int qt(const PII& u)
{
if(u.x>0&&u.y>=0) return 1;
if(u.y>0&&u.x<=0) return 2;
if(u.x<0&&u.y<=0) return 3;
if(u.y<0&&u.x>=0) return 4;
return 0;
}
bool operator<(const PII& u,const PII& v)
{
int i=qt(u),j=qt(v);
return i==j? u*v>0:i<j;
}
const int N=1e5+10,M=N*6,mod=998244353;
int n,m,k,cnt,idx;
PII p[N];
struct edge
{
int from,to,id,val;
}eg[M<<1];
vector<edge> G[N];
void add_edge(int u,int v)
{
eg[cnt]={u,v,cnt,0};
G[u].push_back(eg[cnt]);
cnt++;
}
bool operator<(const edge& u,const edge& v)
{
return p[u.to]-p[u.from]<p[v.to]-p[v.from];
}
int root,fa[M];
ll tag[M][3];
vector<int> T[M];
bool vis[M];
void dfs(int u)
{
vis[u]=true;
for(int v:T[u])
{
if(vis[v]) continue;
fa[v]=u;
tag[v][2]+=tag[u][2];
dfs(v);
}
}
int st[N],tp;
int stk[M],top;
bool ins[M];
ll ksm(ll x,ll y)
{
ll res=1;
while(y)
{
if(y&1) res=res*x%mod;
y>>=1;
x=x*x%mod;
}
return res;
}
ll fac[N],inv[N];
ll C(int x,int y)
{
if(x<y) return 0;
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main()
{
fac[0]=1;
for(int i=1;i<N;i++) fac[i]=fac[i-1]*i%mod;
inv[N-1]=ksm(fac[N-1],mod-2);
for(int i=N-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
ios::sync_with_stdio(false);
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>p[i].x>>p[i].y;
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
add_edge(u,v),add_edge(v,u);
}
for(int i=1;i<=n;i++) sort(G[i].begin(),G[i].end());
for(int i=0;i<cnt;i++)
{
if(eg[i].val) continue;
int now=i;
idx++;
ll s=0;
do
{
s+=p[eg[now].from]*p[eg[now].to];
eg[now].val=idx;
int u=eg[now].to;
auto it=lower_bound(G[u].begin(),G[u].end(),eg[now^1]);
if(it==G[u].begin()) it=G[u].end();
it--;
now=it->id;
}while(now!=i);
if(s<0) root=idx;
}
for(int i=0;i<cnt;i++)
{
int u=eg[i].val,v=eg[i^1].val;
T[u].push_back(v);
}
dfs(root);
for(int o=1;o<=k;o++)
{
cin>>tp;
for(int i=0;i<tp;i++) cin>>st[i];
ll s=0;
for(int i=0;i<tp;i++) s+=p[st[i]]*p[st[(i+1)%tp]];
if(s<0) reverse(st,st+tp);
for(int i=0;i<tp;i++)
{
int u=st[i],v=st[(i+1)%tp];
auto it=lower_bound(G[u].begin(),G[u].end(),(edge){u,v,0,0});
int in=eg[it->id].val,out=eg[it->id^1].val;
tag[it->id^1][1]++;
if(!ins[in]&&fa[in]==out) tag[in][2]++,ins[stk[++top]=in]=true;
else if(!ins[out]&&fa[out]==in) tag[out][2]--,ins[stk[++top]=out]=true;
}
while(top) ins[stk[top--]]=false;
for(int i=0;i<tp;i++)
{
int l=st[(i-1+tp)%tp],mid=st[i],r=st[(i+1)%tp];
auto it1=lower_bound(G[mid].begin(),G[mid].end(),(edge){mid,l,0,0}),
it2=lower_bound(G[mid].begin(),G[mid].end(),(edge){mid,r,0,0});
int tot=G[mid].size();
tag[mid][0]+=(it2-it1+tot)%tot;
}
}
memset(vis,0,sizeof vis);
dfs(root);
for(int i=0;i<cnt;i++)
{
int u=eg[i].from,in=eg[i].val;
tag[u][0]+=tag[in][2];
tag[i][1]+=tag[in][2];
}
for(int i=1;i<=n;i++) tag[i][0]/=G[i].size();
int q;
cin>>q;
while(q--)
{
int z;
cin>>z;
ll ans=0;
for(int i=1;i<=n;i++) ans=(ans+C(tag[i][0],z))%mod;
for(int i=0;i<cnt;i+=2) ans=(ans-C(tag[i][1],z))%mod;
for(int i=1;i<=idx;i++) ans=(ans+C(tag[i][2],z))%mod;
cout<<(ans+mod)%mod<<'\n';
}
return 0;
}