[??记录]AT3537 [ARC083D] Collecting Balls

command_block

2021-05-09 12:57:34

Personal

**题意** : 在平面直角坐标系上有 $2n$ 个小球,第 $i$ 个小球在 $x_i,y_i$ ,满足 $x_i,y_i\in[1,n]∩Z$。 有 $n$ 个 A 类机器人,第 $i$ 个在 $(0,i)$ ,有 $n$ 个 B 类机器人,第 $i$ 个在 $(i,0)$ 。 启动一个 A 类机器人后,它会向右走,将碰到的第一个球收集起来,并消失。 启动一个 B 类机器人后,它会向上走,将碰到的第一个球收集起来,并消失。 只有上一个机器人返回起点后,下一个机器人才会被启动。 求有多少种启动机器人的顺序,能够收集完所有小球。方案数对$10^9+7$取模 。 $n\leq 10^5$ ,时限$\texttt{2s}$。 ------------ ## **Part1** 记第 $i$ 个 A 类机器人为 $A_i$ ,第 $i$ 个 B 类机器人为 $B_i$。 对于球 $(x,y)$ ,它可以被机器人 $A_y$ 或者 $B_x$ 清除。 考虑建立图论模型,连边 $(A_y,B_x)$。对于每条边,必须选择一个端点来将其收集。 不难发现,若某个连通块中 点数 $<$ 边数 ,则无解。又因为本题中 总点数 $=$ 总边数,故图一定是基环树森林。 我们将每条边定向,指向用于收集它的点。不难发现,唯一的方案是定成**基环外向树**,这样每个点的入度都为 $1$。 ## **Part2** 在确定了机器人和球的匹配的基础上,考虑如何处理“机器人每次只会拿最近的球”的约束。 若 $A_i$ 要收集球 $(x,i)$ ,则对于满足 $x'<x$ 的其他球 $(x',i)$ ,必须先收集。 (此时,$x'\neq x$ 的其他球 $(x',i)$ 必然由 B 类机器人收集) 对于 $B$ 也有类似的约束。 若机器人 $u$ 必须先于 $v$ 行动,则连边 $u\rightarrow v$ ,合法排列数即为该有向图的合法拓扑序数。 观察该有向图的性质 : - 每个点只会向右或向上连边,故该图为 $\rm DAG$。 - 对于某个 B 类机器人,只可能向至多一个 A 类机器人连边。A 类机器人类似。 故每个点只有至多一个出边,图为**内向树森林**。 对内向树森林求拓扑序数目是经典问题。 记森林总点数为 $n$ ,节点 $u$ 的子树大小为 $siz_u$ ,则拓扑序数目为 : $$\dfrac{n!}{\prod_{i=1}^nsiz_i}$$ - **证明** : 注意到,森林等价于将所有树连到一个新点上形成的树。 对于节点 $u$ ,设儿子分别为 $v_1,v_2,...,v_m$。记点 $u$ 子树内的拓扑序数目为 $f_u$。 将 $u$ 删除,看做森林。利用多重排列归并儿子的拓扑序,则 : $$f_u=\dfrac{(siz_u-1)!}{\prod_{i=1}^msiz_{v_i}!}\prod_{i=1}^mf_{v_i}$$ 利用上式归纳,假设 $v_{1...m}$ 的子树内都满足结论 : $$ \begin{aligned} f_u&=\dfrac{(siz_u-1)!}{\prod_{i=1}^msiz_{v_i}!}\prod_{i=1}^m\dfrac{siz_{v_i}!}{\prod_{t\text{在}v_i\text{的子树内}}siz_t}\\ &=\dfrac{(siz_u-1)!}{\prod_{i=1}^m\prod_{t\text{在}v_i\text{的子树内}}siz_t}\\ &=\dfrac{siz_u!}{\prod_{t\text{在}u\text{的子树内}}siz_t} \end{aligned} $$ 于是证毕。 ## **Part3** 接下来考虑,对于多种机器人和球之间的匹配,如何求答案的和。 Part1 中发现,生成匹配的图为基环树森林,我们对每棵基环树分别处理,最后用多重排列归并。 对于某棵基环树,将其变为基环内向树的方案数总有两种,因为环有两种方向。 对这两种匹配分别用 Part2 中的算法求解合法排列数即可。 复杂度为 $O(n)$。 ```cpp #include<algorithm> #include<cstdio> #include<vector> #define ll long long #define pb push_back #define MaxN 200500 using namespace std; const int mod=1000000007; ll powM(ll a,int t=mod-2){ ll ret=1; while(t){ if (t&1)ret=ret*a%mod; a=a*a%mod;t>>=1; }return ret; } ll fac[MaxN],ifac[MaxN],inv[MaxN]; void Init(int n) { fac[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod; ifac[n]=powM(fac[n]); for (int i=n;i;i--) ifac[i-1]=ifac[i]*i%mod; for (int i=1;i<=n;i++) inv[i]=ifac[i]*fac[i-1]%mod; } struct UFS{ int f[MaxN];bool vis[MaxN]; void Init(int n) {for (int i=1;i<=n;i++)f[i]=i;} int find(int u) {return f[u]==u ? u : f[u]=find(f[u]);} bool merge(int u,int v){ u=find(u);v=find(v); if (u==v)return 0; f[u]=v;return 1; } }T; vector<int> g[MaxN],id[MaxN]; void adl(int u,int v,int p){ g[u].pb(v);id[u].pb(p); g[v].pb(u);id[v].pb(p); } bool vis[MaxN],fl[MaxN]; int n,f[MaxN],tn,st[MaxN],tn2,st2[MaxN]; void dfs1(int u,int fa) { f[u]=fa; for (int i=0;i<g[u].size();i++) if (g[u][i]!=fa){ st2[++tn2]=id[u][i]; dfs1(g[u][i],u); } } void dfs2(int u) { vis[u]=1; for (int i=0,v;i<g[u].size();i++) if (!vis[v=g[u][i]]){ fl[id[u][i]]=v>n; dfs2(v); } } vector<int> g2[MaxN]; int in[MaxN]; int siz[MaxN]; void dfs3(int u) { siz[u]=1; for (int i=0;i<g2[u].size();i++){ dfs3(g2[u][i]); siz[u]+=siz[g2[u][i]]; } } struct Line{int u,v;bool fl;}l[MaxN]; void adl2(int u,int v){g2[v].pb(u);in[u]++;} ll calc() { for (int i=1;i<=tn2;i++){ int u=st2[i]; if (fl[u]){ int v=l[u].v; for (int j=0;j<g[v].size();j++) if (g[v][j]<l[u].u) adl2(id[v][j],u); }else { int v=l[u].u; for (int j=0;j<g[v].size();j++) if (g[v][j]<l[u].v) adl2(id[v][j],u); } } for (int i=1;i<=tn2;i++) if (!in[st2[i]])dfs3(st2[i]); ll ans=fac[tn2]; for (int i=1;i<=tn2;i++){ ans=ans*inv[siz[st2[i]]]%mod; g2[st2[i]].clear();in[st2[i]]=0; }return ans; } int main() { scanf("%d",&n); T.Init(n+n);Init(n+n); ll ans=1; for (int k=1;k<=n+n;k++){ scanf("%d%d",&l[k].u,&l[k].v);l[k].v+=n; if (T.merge(l[k].u,l[k].v)) adl(l[k].u,l[k].v,k); else { int t=T.find(l[k].u); if (T.vis[t]){puts("0");return 0;} else l[k].fl=T.vis[t]=1; } } for (int k=1;k<=n+n;k++) if (l[k].fl==1){ tn=tn2=0;dfs1(l[k].u,0); for (int p=l[k].v;p;p=f[p])st[++tn]=p; adl(l[k].u,l[k].v,k);st2[++tn2]=k; for (int i=1;i<=tn;i++)vis[st[i]]=1; for (int i=1;i<=tn;i++)dfs2(st[i]); for (int i=1;i<=tn;i++){ int u=st[i]; for (int j=0;j<g[u].size();j++) if (g[u][j]==(i==tn ? st[1] : st[i+1])) fl[id[u][j]]=u>n; } ll sav=calc(); for (int i=1;i<=tn;i++){ int u=st[i]; for (int j=0;j<g[u].size();j++) if (g[u][j]==(i==1 ? st[tn] : st[i-1])) fl[id[u][j]]=u>n; } sav=(sav+calc())%mod; ans=ans*sav%mod*ifac[tn2]%mod; } printf("%lld",ans*fac[n+n]%mod); return 0; } ```