1 条题解
-
0
首先,如果拼接后的绳子绳结之间的距离是相等的,那么必然存在一个 使得对于任意 和任意 ,都有 ,否则一定无解。
下面的推导假设 已经固定,若不存在 则 不固定,这种情况会在之后提及。
不妨设在答案中我们把第 根和第 根绳子拼在了一起,则此时 的最右结点在整根拼好的绳子上的坐标就是前面绳子的长度,而绳子 的第一个结点就是 前面的绳子长度。如果这两个绳结之间的差 ,那么 ,也就是
这就是若 在 后面需要满足的条件。
于是我们对于每一根绳子 ,建一条 的有向边。于是我们只需在图上找一条经过所有边的 Euler 路径或回路,那么就能找到答案了。如果找不到,就是无解。
找 Euler 路径或回路可以用 Hierholzer 算法。
接下来考虑 不固定的情况。考虑找出一些 ,并对于每个 按上述方式建图判断,于是关键在于怎样找出数量尽可能少的 。观察上述的条件,发现若可能出现 Euler 路径或回路,则对于每个结点值 , 和 的差,除了最多一个 和一个 ,剩下的必须是 (若是 Euler 回路则必须全部是 )。
换句话说,起点可重集 和终点可重集 几乎完全相等,最多允许每个集合各多一个元素。不妨设 表示排序后的起点可重集, 表示排序后的 可重集。那么若 几乎完全相同,则大多数元素的配对满足 。也就是说合法的 必然为一个 中的元素减某个 中的元素。
允许最多各多一个的对齐方式非常受限,考虑从左往右对齐的过程:
- 若最小的 和 恰好就能配对,那么 ;
- 若 恰好为多出来的那个,也就是没法配对,则 可能和 配对,于是 ;
- 也可能是 在最左边多出来,那么 可能和 配对,于是 ;
- 也可能 和 都是多出来的,此时 。
对于右端也是类似的,实际上根本不需要额外考虑,因为情况完全对称。
若 固定,时间复杂度 ,不固定的话要乘上一个 。
有点惊险过题,可能是因为人傻常数大。
// Author: Azure_F. #include<iostream> #include<map> #include<vector> #include<utility> #include<cassert> #include<algorithm> #include<cstring> constexpr int N = 1e5 + 5; int n, s[N], p[N][2], u[N], v[N], a[N], b[N]; int in[N << 1], out[N << 1]; std::vector<std::pair<int, int> > G[N << 1]; int ans[N], la = 0; bool used[N]; void dfs(int u) { while(!G[u].empty()) { auto [v, id] = G[u].back(); G[u].pop_back(); if(!used[id]) used[id] = 1, dfs(v), ans[++la] = id; } } inline bool _chk(int d) { std::map<int, int> S; for(int i = 1; i <= n; ++i) u[i] = p[i][0], v[i] = p[i][1] - s[i] + d; for(int i = 1; i <= n; ++i) S[u[i]] = S[v[i]] = 1; int l = 0; for(auto& [u, v] : S) v = ++l; for(int i = 1; i <= n; ++i) { G[S[u[i]]].emplace_back(S[v[i]], i); ++out[S[u[i]]], ++in[S[v[i]]]; } int st = -1, ed = -1; for(int i = 1; i <= l; ++i) { if(out[i] - in[i] == 1) { if(!(~st)) st = i; else return false; } else if(in[i] - out[i] == 1) { if(!(~ed)) ed = i; else return false; } else if(in[i] ^ out[i]) return false; } if(!(~st)) for(int i = 1; i <= l; ++i) if(out[i]) { st = i; break; } dfs(st); return !(la ^ n); } inline bool chk(int d) { bool ret = _chk(d); std::memset(in, 0, sizeof in); std::memset(out, 0, sizeof out); std::memset(used, 0, sizeof used); for(int i = 0; i < (N << 1); ++i) G[i].clear(); return ret; } inline int Yes() { std::cout << "Yes\n"; for(int i = la; i >= 1; --i) std::cout << ans[i] << " "; return 0; } inline int No() { std::cout << "No\n"; return 0; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int d = -1; std::cin >> n; for(int i = 1; i <= n; ++i) { int m, lp; std::cin >> m >> s[i]; for(int j = 1; j <= m; ++j) { int _p; std::cin >> _p; j == 1 ? p[i][0] = _p : 0; j == m ? p[i][1] = _p : 0; if((~d) && j > 1 && _p - lp != d) return No(); else if(!(~d) && j > 1) d = _p - lp; lp = _p; } } if(~d) return chk(d) ? Yes() : No(); else { for(int i = 1; i <= n; ++i) a[i] = p[i][0], b[i] = p[i][1] - s[i]; std::sort(a + 1, a + n + 1); std::sort(b + 1, b + n + 1); if(n == 1) return chk(a[1] - b[1]) ? Yes() : No(); const int d[4] = {a[1] - b[1], a[2] - b[1], a[1] - b[2], a[2] - b[2]}; for(int i = 0; i < 4; ++i) if(chk(d[i])) return Yes(); else la = 0; return No(); } return 0; }
- 1
信息
- ID
- 10279
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者