[ARC083]Collecting Balls
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\)。
\]
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的更多相关文章
- [ARC083F] Collecting Balls [建二分图+环套树定向+建拓扑图+树的拓扑序计数]
题面 [传送门](https://arc083.contest.atcoder.jp/tasks/arc083_d) 思路 这是一道真正的好题 第一步:转化模型 行列支配类的问题,常见做法就是把行和列 ...
- 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, ...
- 【AtCoder Beginner Contest 074 B】Collecting Balls (Easy Version)
[链接]h在这里写链接 [题意] 看懂题目之后就会发现是道大水题. [题解] 在这里写题解 [错的次数] 0 [反思] 在这了写反思 [代码] #include <bits/stdc++.h&g ...
- 题解-AtCoder ARC-083F Collecting Balls
Problem ARC083F 题意概要:给定 \(2n\) 个二维平面上的球,坐标分别为 \((x_i,y_i)\),并给出 \(n\) 个 \(A\)类 机器人 和 \(n\) 个 \(B\)类 ...
- [atARC083F]Collecting Balls
考虑一个构造,对于坐标$(x,y)$,连一条$x$到$y$的边(注意:横坐标和纵坐标即使权值相同也是不同的点),之后每一个连通块独立,考虑一个连通块内部: 每一个点意味着一次删除操作,每一个边意味着一 ...
- 【AtCoder】ARC083
C - Sugar Water 计算一下可以达到水是多少,可以到达的糖是多少 枚举水,然后加最多能加的糖,是\(min(F - i *100,E * 100)\),计算密度,和前一个比较就行 #inc ...
- Atcoder 乱做
最近感觉自己思维僵化,啥都不会做了-- ARC103 F Distance Sums 题意 给定第 \(i\) 个点到所有点的距离和 \(D_i\) ,要求构造一棵合法的树.满足第 \(i\) 个点到 ...
- AtCoder刷题记录
构造题都是神仙题 /kk ARC066C Addition and Subtraction Hard 首先要发现两个性质: 加号右边不会有括号:显然,有括号也可以被删去,答案不变. \(op_i\)和 ...
- POJ2096 Collecting Bugs
Time Limit: 10000MS Memory Limit: 64000K Total Submissions: 5090 Accepted: 2529 Case Time Limit: ...
随机推荐
- 树形dp专栏
前言 自己树形dp太菜了,要重点搞 219D Choosing Capital for Treeland 终于自己做了一道不算那么毒瘤的换根dp 令 \(f[u]\) 表示以 \(u\) 为根,子树内 ...
- [python 学习] 使用 xml.etree.ElementTree 模块处理 XML
---恢复内容开始--- 导入数据(读文件和读字符串) 本地文件 country_data.xml <?xml version="1.0"?> <data> ...
- 【leetcode】1091. Shortest Path in Binary Matrix
题目如下: In an N by N square grid, each cell is either empty (0) or blocked (1). A clear path from top- ...
- sql2000如何完美压缩.mdf文件
--收缩数据库 dbcc shrinkdatabase('test_db') , ) --数据文件mdf , ) --日志文件ldf ) --更新
- C#防止窗口重复打开
修改Program.cs文件 using System; using System.Collections.Generic; using System.Linq; using System.Windo ...
- UE4开发PSVR游戏,使用RazorGPU进行调试的方法
打开Project Launcher,如果没有需要的Devkit平台目标,就点下方的Device Manager,右键Claim 相应的目标.如果已经出现在Project Launcher中,点击上方 ...
- IntelliJ IDEA 装配FindBugs以及应用
IntelliJ IDEA 安装FindBugs以及应用 众所周知,项目越来越大,开发人员越来越多,我们的代码审查工作会变得越来越复杂,对代码质量控制难度也与日俱增,尽管经验丰富的程序员能审查能检查出 ...
- linux之rpm软件包管理
1.RPM包的命名规则 例如:httpd-2.2.15-15.el6.centos.1.i686.rpm httpd · 软件包名 2.2.15 软件版本 15 ...
- PHP实现多服务器SESSION共享
为什么要session共享? 现在稍微大一点的网站基本上都有好几个子域名,比如www.feiniu.com, search.feiniu.com, member.feiniu.com,这些网站如果需要 ...
- Session实现验证码登陆笔记
1.生成验证码Servlet package com.isit.servlet; import javax.imageio.ImageIO; import javax.servlet.ServletE ...