Description

有一个 \(n\times n\) 的矩阵,矩阵内有 \(2n\) 个球。对于 \(i \in [1,n]\) ,\((0,i) (i,0)\) 的位置各有一个启动后往右走/往上走的机器人,机器人撞到球会和球一起消失。问启动机器人顺序的方案数,满足所有球最后都消失。

\(n \le 10^{5}\)

Solution

先建图,对于平面上的一个 \((x, y)\) 位置的球,把机器人看做点,球看做边,连一条 \(x\) 到 \(y + n\) ,权值为 \(x + y\) 的边。

然后问题就转化成了在这张图上排列点的操作顺序使最后所有边都被删除,一个点操作是指将和它相连的边中最小的一条边删除。

不难发现这个图是一个基环树森林,那么不在环上的点每个点要删除的边是固定的,在环上的点就有两种选择,每个点顺时针和逆时针删。

我们可以进一步构造模型,现在每个点都有一个要删除的边,把每个点u要删除边权记为val[u],定义为u的点权,那么在原图的基础上再建一个新图:每个点向和他连边的点中权值小于它自己权值的点连新边(不考虑自己分配到的边所指向的那个点),这样建出来的新图是原图边集的一个子集,并且是一个森林,现在问题又转换成求每个点的父亲要比自己先被操作的方案数,这就是一个很经典的问题了,dfs求出size计数即可,大概写一下转移。

假设有3个儿子 \(v_1, v_2, v_3\)。

\[f[u] = {size[v_1] + size[v_2]\choose size[v_1]} {size[v_1] + size[v_2] + size[v_3]\choose size[v_1] + size[v_2]} \times f[v_2]\times f[v_3] \times f[v_3]
\]

Code

#include <iostream>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream> typedef long long LL;
typedef unsigned long long uLL; #define PII pair<int, int>
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DE(x) cerr << x << endl;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;
#define rep(i, a, b) for (register int (i) = (a); (i) <= (b); ++(i)) using namespace std; inline void proc_status()
{
ifstream t("/proc/self/status");
cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
inline int read()
{
register int x = 0; register int f = 1; register char c;
while (!isdigit(c = getchar())) if (c == '-') f = -1;
while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
return x * f;
}
template<class T> inline void write(T x)
{
static char stk[30]; static int top = 0;
if (x < 0) { x = -x, putchar('-'); }
while (stk[++top] = x % 10 xor 48, x /= 10, x);
while (putchar(stk[top--]), top);
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; } const int maxN = (int) 2e5;
const int mod = (int) 1e9 + 7; struct Edge
{
int v, w;
Edge() { }
Edge(int v, int w) : v(v), w(w) { }
} ; int n;
bool vis[maxN + 2], instk[maxN + 2], found, in_cir[maxN + 2], choose[maxN + 2];
int top, val[maxN + 2], par[maxN + 2], size[maxN + 2];
PII stk[maxN + 2];
vector<pair<int, int> > circle;
vector<int> node;
vector<Edge> g[maxN + 2]; namespace math
{
int fac[2 * maxN + 2], ifac[2 * maxN + 2];
LL qpow(LL a, LL b)
{
LL ans = 1;
while (b)
{
if (b & 1)
ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
void init()
{
fac[0] = 1;
for (int i = 1; i <= 2 * n; ++i) fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[2 * n] = qpow(fac[2 * n], mod - 2);
for (int i = 2 * n - 1; i >= 0; --i) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
}
int C(int n, int m) { return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod; }
int merge(int a, int b) { return C(a + b, a); }
}
using namespace math; void input()
{
n = read() << 1;
for (int i = 1; i <= n; ++i)
{
int x = read(), y = read();
g[x].emplace_back(y + n / 2, x + y);
g[y + n / 2].emplace_back(x, x + y);
choose[x] = choose[y + n / 2] = 1;
}
} void dfs_circle(int x, int fa, int edge)
{
if (found) return;
stk[++top] = MP(x, edge);
instk[x] = 1;
for (Edge E : g[x])
{
int v = E.v, w = E.w;
if (v == fa) continue;
if (!instk[v])
{
dfs_circle(v, x, w);
}
else
{
int To = v;
do
{
v = stk[top].first;
in_cir[v] = 1;
circle.push_back(stk[top--]);
} while (v != To);
circle.back().second = w;
found = 1;
return;
}
if (found) return;
}
instk[x] = 0;
top--;
} void dfs1(int u, int fa)
{
vis[u] = 1;
node.push_back(u);
for (Edge E : g[u])
{
int v = E.v, w = E.w;
if (v != fa and !in_cir[v])
{
val[v] = w;
dfs1(v, u);
}
}
} int dfs2(int u)
{
int ans = 1;
size[u] = 0;
for (Edge E : g[u])
{
int v = E.v;
if (par[v] != u) continue;
int cur = dfs2(v);
ans = 1ll * ans * cur % mod * merge(size[u], size[v]) % mod;
size[u] += size[v];
}
size[u]++;
return ans;
} int calc()
{
for (int u : node)
{
size[u] = 0;
par[u] = 0;
}
for (int u : node)
{
for (Edge E : g[u])
{
int v = E.v, w = E.w;
if (w < val[u]) par[v] = u;
}
}
int SIZE = 0, ans = 1;
for (int u : node)
{
if (!par[u])
{
int cur = dfs2(u);
ans = 1ll * ans * cur % mod * merge(SIZE, size[u]) % mod;
SIZE += size[u];
}
}
return ans;
} void solve()
{
int cnt = 0;
for (int i = 1; i <= n; ++i)
cnt += choose[i];
if (cnt != n)
{
cout << 0 << endl;
return;
}
int SIZE = 0, ans = 1;
for (int i = 1; i <= n && ans; ++i)
{
if (!vis[i])
{
int cur = 0;
top = 0;
found = 0;
circle.clear();
dfs_circle(i, 0, 0); node.clear();
for (int j = 0; j < SZ(circle); ++j)
dfs1(circle[j].first, 0); for (int j = 0; j < SZ(circle); ++j)
val[circle[j].first] = circle[j].second;
(cur += calc()) %= mod; circle.push_back(circle[0]);
for (int j = 1; j < SZ(circle); ++j)
val[circle[j].first] = circle[j - 1].second;
(cur += calc()) %= mod; ans = 1ll * ans * cur % mod * merge(SIZE, node.size()) % mod;
SIZE += node.size();
}
}
printf("%d\n", ans);
} int main()
{
#ifndef ONLINE_JUDGE
freopen("AGC083F.in", "r", stdin);
freopen("AGC083F.out", "w", stdout);
#endif
input();
math::init();
solve();
return 0;
}

[ARC083]Collecting Balls的更多相关文章

  1. [ARC083F] Collecting Balls [建二分图+环套树定向+建拓扑图+树的拓扑序计数]

    题面 [传送门](https://arc083.contest.atcoder.jp/tasks/arc083_d) 思路 这是一道真正的好题 第一步:转化模型 行列支配类的问题,常见做法就是把行和列 ...

  2. Arc083_F Collecting Balls

    传送门 题目大意 给定$N$,在$(1,0),(2,0)......(N,0)$和$(0,1),(0,2)...(0,N)$上都有$1$个机器人,同时给定$2N$个坐标$(x,y),x,y\in[1, ...

  3. 【AtCoder Beginner Contest 074 B】Collecting Balls (Easy Version)

    [链接]h在这里写链接 [题意] 看懂题目之后就会发现是道大水题. [题解] 在这里写题解 [错的次数] 0 [反思] 在这了写反思 [代码] #include <bits/stdc++.h&g ...

  4. 题解-AtCoder ARC-083F Collecting Balls

    Problem ARC083F 题意概要:给定 \(2n\) 个二维平面上的球,坐标分别为 \((x_i,y_i)\),并给出 \(n\) 个 \(A\)类 机器人 和 \(n\) 个 \(B\)类 ...

  5. [atARC083F]Collecting Balls

    考虑一个构造,对于坐标$(x,y)$,连一条$x$到$y$的边(注意:横坐标和纵坐标即使权值相同也是不同的点),之后每一个连通块独立,考虑一个连通块内部: 每一个点意味着一次删除操作,每一个边意味着一 ...

  6. 【AtCoder】ARC083

    C - Sugar Water 计算一下可以达到水是多少,可以到达的糖是多少 枚举水,然后加最多能加的糖,是\(min(F - i *100,E * 100)\),计算密度,和前一个比较就行 #inc ...

  7. Atcoder 乱做

    最近感觉自己思维僵化,啥都不会做了-- ARC103 F Distance Sums 题意 给定第 \(i\) 个点到所有点的距离和 \(D_i\) ,要求构造一棵合法的树.满足第 \(i\) 个点到 ...

  8. AtCoder刷题记录

    构造题都是神仙题 /kk ARC066C Addition and Subtraction Hard 首先要发现两个性质: 加号右边不会有括号:显然,有括号也可以被删去,答案不变. \(op_i\)和 ...

  9. POJ2096 Collecting Bugs

    Time Limit: 10000MS   Memory Limit: 64000K Total Submissions: 5090   Accepted: 2529 Case Time Limit: ...

随机推荐

  1. HTML替换元素,非替换元素和控制元素

    替换元素:元素内容由标签的属性来设置,标签其实就是一个占位符.替换元素因为元素内容来自外部资源,所以这些标签大多具有src,指明要引入的资源路径,所以大多仅需要一个标签就可以.例如:<link ...

  2. Linux性能优化从入门到实战:15 文件系统篇:磁盘 I/O

    磁盘   磁盘是可以持久化存储的设备,按照存储介质来分类:   (1)机械磁盘(硬盘驱动器,Hard Disk Driver,HDD),主要由盘片和读写磁头组成,数据就存储在盘片的环状磁道中.在读写数 ...

  3. UML快速理解

    在团队协作过程中最常见的就是开会.开会最常用的就是图,而图中最常见的就是流程图.时序图.类图,这三个图可以清楚的描述你想解释的内容.学好类图不仅仅能帮助自己更清楚的梳理业务,还能提高开会效率. 上图是 ...

  4. 解决 pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '127.0.0.1' ([Errno 61] Conne

    pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '127.0.0.1' ([Errno 61] ...

  5. 用于DataLoader的pytorch数据集

    暂时介绍 image-mask型数据集, 以人手分割数据集 EGTEA Gaze+ 为例. 准备数据文件夹 需要将Image和Mask分开存放, 对应文件的文件名必须保持一致. 提醒: Mask 图像 ...

  6. Python实例教程

    转自:http://codingdict.com/article/9026 Python 100例-01 题目: 输有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数? Python 1 ...

  7. XCODE真机调试No Devices Registered

    百思不得期解,摸索发现是由于没有选择真机设备的原因, 在Xcode左上角选择真机设备即可.

  8. css3新增的属性 - 分享

    CSS3新增属性   一.transform变换效果 CSS3 提供了元素变形效果,也叫做变换.它可以将元素实现旋转.缩放和平移的功能. 属性有两个:transform 和 transform-ori ...

  9. Python3解leetcode First Bad Version

    问题描述: You are a product manager and currently leading a team to develop a new product. Unfortunately ...

  10. tensorflow2 矩阵乘法问题

    tensorflow2再做矩阵相乘的时候如果维度有None,则矩阵相乘结果不对. 也不知道为什么,只能用矩阵对应元素相乘在相加的形式来实现矩阵相乘,这个困扰了我好几天,所以记下来. inputs_ti ...