2019-2020 ICPC Asia Taipei-Hsinchu Regional Contest 部分题解
2019-2020 ICPC Asia Taipei-Hsinchu Regional Contest
A. Rush Hour Puzzle
队友写的暴搜+剪枝。
#include<bits/stdc++.h>
#define ll long long
#define maxn 2010
#define mod 998244353
using namespace std;
struct cv {
int mp[6][6];
int mov;
friend bool operator<(cv p, cv q) {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
if (p.mp[i][j] != q.mp[i][j]) return p.mp[i][j] < q.mp[i][j];
}
}
return p.mp[0][0] < q.mp[0][0];
}
}dd, hf;
int to[4][2] = { 0,1,0,-1,1,0,-1,0 };
int main() {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
scanf("%d", &dd.mp[i][j]);
}
}
dd.mov = 0;
queue<cv>q;
q.push(dd);
set<cv>st;
st.insert(dd);
while (!q.empty()) {
dd = q.front();
q.pop();
if (dd.mp[2][5] == 1) {
int cnt = 1;
for (int i = 4; i >= 0; i--) {
if (dd.mp[2][i] != 1) break;
cnt++;
}
int ans = (dd.mov + cnt);
if (ans > 10) ans = -1;
printf("%d\n", ans);
return 0;
}
if (dd.mov == 10) break;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
if (dd.mp[i][j] == 0) {
for (int k = 0; k < 4; k++) {
int x = i + to[k][0], y = j + to[k][1];
if (x < 0 || x >= 6 || y < 0 || y >= 6 || dd.mp[x][y] == 0) {
continue;
}
int xx = x + to[k][0], yy = y + to[k][1];
if (xx < 0 || xx >= 6 || yy < 0 || yy >= 6 || dd.mp[x][y] != dd.mp[xx][yy]) {
continue;
}
while (xx >= 0 && xx < 6 && yy >= 0 && yy < 6 && dd.mp[x][y] == dd.mp[xx][yy]) {
xx = xx + to[k][0], yy = yy + to[k][1];
}
xx = xx - to[k][0], yy = yy - to[k][1];
hf = dd;
hf.mov++;
swap(hf.mp[i][j], hf.mp[xx][yy]);
if (!st.count(hf)) {
st.insert(hf);
q.push(hf);
}
}
}
}
}
}
printf("-1\n");
return 0;
}
C. Are They All Integers?
题意:给定一个数组 \(a_n\) ,判断是否所有的三元集 \((i,j,k)\) 均满足 \(\frac{a_i-a_j}{a_k}\) 为整数。
分析:\(n\) 很小,直接暴力。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 55;
int a[maxn];
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
bool flag = true;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= n; k++)
if (i != j && j != k && i != k && (a[i] - a[j]) % a[k])
flag = false;
if (flag)
printf("yes\n");
else
printf("no\n");
return 0;
}
D. Tapioka
签到。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
int main() {
io(); string s;
bool f = false;
for (int i = 1; i <= 3; ++i) {
string x; cin >> x;
if (x != "bubble" && x != "tapioka") {
if (f) s += " ";
s += x;
f = true;
}
}
if (s == "") s = "nothing";
cout << s;
}
E. The League of Sequence Designers
队友秒了。
#include <bits/stdc++.h>
using namespace std;
int a[2005];
int main()
{
int t;
scanf("%d", &t);
while (t--) {
int k, len;
scanf("%d%d", &k, &len);
if (len >= 2000) {
printf("-1\n");
continue;
}
a[1] = -1;
int cnt = (1999 + k) % 1998;
fill(a + 2, a + 2000, (1999 + k) / 1998);
a[1999] += cnt;
printf("1999\n");
for (int i = 1; i <= 1999; i++)
printf("%d ", a[i]);
printf("\n");
}
return 0;
}
H. Mining a
题意:对于一个给定的 \(n\) 求出最大的正整数 \(a\) ,满足 \(\frac{1}{n}=\frac{1}{a\oplus b}+\frac{1}{b}\) ,其中 \(b\) 是一个任意的正整数。
分析:猜结论,由这个基础的变换 \(\frac{1}{n}=\frac{1}{n}-\frac{1}{n+1}+\frac{1}{n+1}=\frac{1}{n(n+1)}+\frac{1}{n+1}\) 直接猜 \(a=n(n+1)\oplus (n+1)\) 。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
int main() {
io(); int t;
cin >> t;
while (t--) {
ll n; cin >> n;
ll ans = (n * (n + 1ll) ^ (n + 1ll));
cout << ans << '\n';
}
}
J. Automatic Control Machine
队友秒了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
char s[maxn];
bitset<maxn> p[20];
int main()
{
int t;
scanf("%d", &t);
while (t--) {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%s", s + 1);
p[i].reset();
for (int j = 1; j <= n; j++)
if (s[j] == '1')
p[i].set(j);
}
int base = (1 << m) - 1, ans = 20;
for (int i = 0; i <= base; i++) {
bitset<maxn> cnt;
int k = 0;
for (int j = 0; j < m; j++)
if (i & (1 << j))
cnt |= p[j + 1],
k++;
if ((int)cnt.count() == n)
ans = min(ans, k);
}
if (ans == 20)
printf("-1\n");
else
printf("%d\n", ans);
}
return 0;
}
K. Length of Bundle Rope
队友秒了。
#include<bits/stdc++.h>
#define ll long long
#define maxn 10010
#define mod 998244353
using namespace std;
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
priority_queue<int, vector<int>, greater<int> >s;
for (int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
s.push(x);
}
int ans = 0;
for (int i = 0; i < n - 1; i++) {
int x = s.top();
s.pop();
int y = s.top();
s.pop();
ans += x + y;
s.push(x + y);
}
printf("%d\n", ans);
}
return 0;
}
L. Largest Quadrilateral
题意:给定平面上 \(n\) 个点,求平面最大四边形。
分析:显然先把凸包跑出来,最大四边形的顶点必定在凸包上。但是这题非常坑,数据中有重点,并且如果凸包上只有三个点,那么会构成凹四边形。排除这些特判后就是个旋转卡壳了,像求平面最大三角形那样旋转即可,因为四边形可以看作是两个三角形,我们以四边形对角线为这两个三角形的底边,就能写出一个 \(O(n^2)\) 的旋转卡壳了。
#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma comment(linker, "/stack:200000000")
#include <bits/stdc++.h>
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
int num;
struct Point {
ll x, y;
Point() {}
Point(ll xx, ll yy) :x(xx), y(yy) {}
Point operator + (const Point& k1) const { return Point(k1.x + x, k1.y + y); }
Point operator - (const Point& k1) const { return Point(x - k1.x, y - k1.y); }
Point operator * (ll k1) const { return Point(x * k1, y * k1); }
Point operator / (ll k1) const { return Point(x / k1, y / k1); }
bool operator < (const Point& b) const {
return (x < b.x) || (x == b.x && y < b.y);
}
bool operator ==(const Point& b) const {
return (x == b.x) && (y == b.y);
}
ll abs() { return x * x + y * y; }
ll dis(Point k1) { return ((*this) - k1).abs(); }
};
ll cross(Point a, Point b) { return a.x * b.y - a.y * b.x; } //叉积
ll dot(Point a, Point b) { return a.x * b.x + a.y * b.y; } //点积
ll area(Point a, Point b, Point c) { return abs(cross(a - b, a - c)); }
vector<Point> ConvexHull(vector<Point>A, int flag = 1) { // flag = 0 不严格 flag = 1 严格
int n = A.size(); vector<Point>ans(n * 2);
sort(A.begin(), A.end()); int now = -1;
for (int i = 0; i < A.size(); i++) {
while (now > 0 && cross(ans[now] - ans[now - 1], A[i] - ans[now - 1]) < flag) now--;
ans[++now] = A[i];
}
int pre = now;
for (int i = n - 2; i >= 0; i--) {
while (now > pre&& cross(ans[now] - ans[now - 1], A[i] - ans[now - 1]) < flag) now--;
ans[++now] = A[i];
}
ans.resize(now);
return ans;
}
inline int nxt(int i) { return i == num - 1 ? 0 : i + 1; }
ll Rotating_calipers(int n, vector<Point>& p) {
ll ans = 0;
for (int i = 0; i < n; i++) {
int p1 = nxt(i);
int p2 = nxt(nxt(nxt(i)));
for (int j = nxt(nxt(i)); nxt(j) != i; j = nxt(j)) {
while (nxt(p1) != j && area(p[i], p[nxt(p1)], p[j]) > area(p[i], p[p1], p[j]))
p1 = nxt(p1);
if (p2 == j) p2 = nxt(p2);
while (nxt(p2) != i && area(p[j], p[nxt(p2)], p[i]) > area(p[j], p[p2], p[i]))
p2 = nxt(p2);
ans = max(ans, area(p[i], p[p1], p[j]) + area(p[j], p[p2], p[i]));
}
}
return ans;
}
int main() {
io(); int t;
cin >> t;
while (t--) {
int n; cin >> n;
vector<Point> p(n);
for (auto& i : p) cin >> i.x >> i.y;
vector<Point> ch = ConvexHull(p, 0);
num = ch.size();
ll maxs = 0;
if (ch.size() < 3) maxs = 0;
else if (ch.size() == 3) {
ll s = area(ch[0], ch[1], ch[2]);
for (auto i : p) {
if (i == ch[0] || i == ch[1] || i == ch[2]) continue;
ll mins = min(area(i, ch[1], ch[2]), min(area(ch[0], i, ch[2]),
area(ch[0], ch[1], i)));
maxs = max(maxs, s - mins);
}
}
else maxs = Rotating_calipers(ch.size(), ch);
cout << ((maxs & 1ll) ? to_string(maxs / 2ll) + ".5\n" : to_string(maxs / 2ll) + "\n");
}
}
M. DivModulo
题意:给定正整数 \(N,M,D\) 求 \(C_M^N\ dmod\ {D}\) 。其中 \(x\ dmod\ y\) 指的是去掉 \(x\) 中所有 \(y\) 因子后取余数,假设 \(x=z\times y^k(y\nmid z)\) ,那么 \(x\ dmod\ y=z\mod{y}\) 。
分析:根据表述习惯,我们改写为求解 \(C_N^M\ dmod\ {D}\) 。然后记 \(x\ div\ y\) 为去掉 \(x\) 中所有 \(y\) 因子。则有:
因此若上式分母与 \(D\) 互质,则
其中 \(inv\) 表示模 \(D\) 的逆元,这个问题只需要简单的分段就能解决了。
若上式分母与 \(D\) 不互质,我们先将 \(D\) 的质因子分解:\(D=D_1D_2\cdots D_s=p_1^{a_1}p_2^{a_2}\cdots p_s^{a_s}\) ,其中 \(p_i\) 为质因子。于是可以推出:
因此我们可以得到 \(K=\underset{i\in[1,s]}{\min{\frac{k_i}{a_i}}}\) ,然后推出:
由上式,我们对于所有 \(i\in[1,s]\) 都能求出 \(C^M_N\ div\ D\mod{D_i}\) 的值,因为该式中分母与模数互质,逆元存在,然后就能通过扩展中国剩余定理求出 \(C^M_N\ div \ D\mod{D}\) 。
那么最后还剩下一个问题,怎么求 \(C^M_N\ div\ D\mod{D_i}\) ,该问题等价于求 \(N!\ div\ D\mod{P}\) 。
我们将 \(N!\) 分成两部分考虑:\(N!=\underset{D\mid i}{\prod^N_{i=1}}{i}\times \underset{D\nmid i}{\prod^N_{i=1}}{i}\) ,即把能整除 \(D\) 的 \(i\) 分为一类,不能整除的分为一类。
因此:\(N!\ div\ D\mod{P}=\lfloor \frac{N}{D}\rfloor !\times (\underset{D\nmid i}{\prod^P_{i=1}}{i})^{\lfloor \frac{N}{P}\rfloor}\times\underset{D\nmid i}{\prod^{P\%N}_{i=1}}{i}\mod{P}\)
观察该式不难发现,对于 \((\underset{D\nmid i}{\prod^P_{i=1}}{i})^{\lfloor \frac{N}{P}\rfloor}\times\underset{D\nmid i}{\prod^{P\%N}_{i=1}}{i}\mod{P}\) 这部分的值,我们只需要 \(O(P)\) 的预处理后就能 \(O(1)\) 查询,然后 \(N!\) 每次都能够转化为 \(\lfloor \frac{N}{D}\rfloor !\) 的子问题。由于 \(D\leq1.6e7\) ,我们可以计算出任意一个 \(1.6e7\) 以内的数字最多含有 \(8\) 个质因子,因此粗略地估计一下,复杂度上限为 \(O(8(D+\log_{D}{N}))\) ,当然不可能达到这个复杂度。
#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma comment(linker, "/stack:200000000")
#include <bits/stdc++.h>
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
ll M, N, D, ans;
namespace Pollard_Rho {
map<ll, ll> MP;
inline ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
inline ll mulmod(ll x, ll y, const ll z) {
return (x * y - (ll)(((long double)x * y + 0.5) / (long double)z) * z + z) % z;
}
inline ll powmod(ll a, ll b, const ll mo) {
ll s = 1;
for (; b; b >>= 1, a = mulmod(a, a, mo)) if (b & 1) s = mulmod(s, a, mo);
return s;
}
bool isPrime(ll p) { // Miller-Rabin
const int lena = 10, a[lena] = { 2,3,5,7,11,13,17,19,23,29 };
if (p == 2) return true;
if (p == 1 || !(p & 1) || (p == 46856248255981ll)) return false;
ll D = p - 1; while (!(D & 1)) D >>= 1;
for (int i = 0; i < lena && a[i] < p; i++) {
ll d = D, t = powmod(a[i], d, p); if (t == 1) continue;
for (; d != p - 1 && t != p - 1; d <<= 1) t = mulmod(t, t, p);
if (d == p - 1) return false;
}
return true;
}
void reportFactor(ll n) { // 得到一个素因子
MP.count(n);
MP[n]++;
}
ll ran() { return rand(); } // 随机数
void getFactor(ll n) { // Pollard-Rho
if (n == 1) return;
if (isPrime(n)) { reportFactor(n); return; }
while (true) {
ll c = ran() % n, i = 1, x = ran() % n, y = x, k = 2;
do {
ll d = gcd(n + y - x, n);
if (d != 1 && d != n) { getFactor(d); getFactor(n / d); return; }
if (++i == k) y = x, k <<= 1;
x = (mulmod(x, x, n) + c) % n;
} while (y != x);
}
}
}
using namespace Pollard_Rho;
void exgcd(ll a, ll b, ll& x, ll& y) {
if (!b) x = 1, y = 0;
else exgcd(b, a % b, y, x), y -= a / b * x;
}
ll qpow(ll a, ll b, ll mod) {
ll ans = 1;
while (b) {
if (b & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return ans;
}
ll calc_divs(ll m, ll mod) {
ll ans = 0;
for (ll i = mod; ; i *= mod) {
ans += m / i;
if ((long double)i * mod > m) return ans;
}
}
ll calc_fac(ll p, ll mod, vector<ll>& fac) {
if (p == 0) return 1;
return calc_fac(p / mod, mod, fac) * qpow(fac[mod - 1], p / mod, mod) % mod * fac[p % mod] % mod;
}
ll getinv(ll n, ll mod) {
ll x, y;
exgcd(n, mod, x, y);
return (x + mod) % mod;
}
ll excrt(ll a, ll m, ll b, ll n) {
ll x, y; exgcd(m, n, x, y);
ll ret = a * (y + m) % m * n + b * (x + n) % n * m;
if (ret >= m * n) ret -= m * n;
return ret;
}
int main() {
io(); cin >> N >> M >> D;
getFactor(D);
vector<ll> ki;
ll K = 1e18;
for (auto it : MP) {
ll p = it.first;
ki.emplace_back(calc_divs(N, p) - calc_divs(M, p) - calc_divs(N - M, p));
K = min(ki.back() / it.second, K);
}
int cnt = 0;
ll fmod = 1;
for (auto it : MP) {
ll p = it.first, a = it.second;
ll cur = qpow(p, a, (ll)1e18);
ll divs = ki[cnt] - K * a;
vector<ll> val(cur);
vector<ll> fac(cur);
for (int i = 1; i < cur; ++i) val[i] = i;
for (ll i = p; i * p <= cur; i *= p)
for (ll j = i; j < cur; j += i)
val[j] /= p;
val[0] = fac[0] = 1;
for (int i = 1; i < cur; ++i) fac[i] = fac[i - 1] * val[i] % cur;
ll res = calc_fac(N, cur, fac) * getinv(calc_fac(M, cur, fac), cur) % cur
* getinv(calc_fac(N - M, cur, fac), cur) % cur
* getinv(qpow(D / cur, K, cur), cur) % cur;
ans = excrt(ans, fmod, res * qpow(p, divs, cur) % cur, cur);
fmod *= cur;
++cnt;
}
cout << ans;
}