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. JVM内存模型及参数调优

    堆.栈.方法区概念区别 1.堆 堆内存用于存放由new创建的对象和数组.在堆中分配的内存,由java虚拟机自动垃圾回收器来管理.根据垃圾回收机制的不同, Java堆有可能拥有不同的结构,最为常见的就是 ...

  2. 菜鸟程序员成长史 --记 Github 1000+ contributions

    其实一直以来想写一篇文章总结这几年的技术学习,刚好趁着自己的第一次github contribution 达到1000+,写篇文章总结以下.本文篇幅较长,我会分为几个章节来分别阐述. 博客篇 为什么我 ...

  3. 彻底解决mysql报错:1030, 'Got error 28 from storage engine'

    权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/harry5508/article/deta ...

  4. UNIX网络编程总结四

    socket: 为了执行网络I/O,一个进程做的第一件事就是调用socket函数. family指明协议族,type指明类型,除非在原始套接口,protocol一般为0,并非所有的family,typ ...

  5. alert(1) to win 11

  6. sqlmap POST注入

    带表单的页面: 1.sqlmap.py -u "http://mysqli/Less-11/" --forms 2.python sqlmap.py -r d:\test.txt ...

  7. python 小工具 重命名当前文件夹内所有的文件,升序命名

    背景:一个朋友想升序重命名他的照片,但是太多了不想手动所以,emememem os这个模块,不用说,rename,filedir等 #conding=utf8 import os path = os. ...

  8. super语句不必须放在方法第一行。

    class A(object): pass class B(A): def __init__(self): self.__a = "B#a" super(B, self).__in ...

  9. Springboot ,1开启配置与2.扫描包(控制层,service层)二个注解@EnableAutoConfiguration,@ComponentScan 合并成一个注解@SpringBootApplication

    //@EnableAutoConfiguration//@ComponentScan(value= {"com.foen.cloud.controller.*","com ...

  10. python 全栈开发,Day45(html介绍和head标签,body标签中相关标签)

    一.html介绍 1.web标准 web准备介绍: w3c:万维网联盟组织,用来指定web标准的机构(组织) web标准:制作网页遵循的规范 web准备规范的分类:结构标准.表现标准.行为标准. 结构 ...