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\) 因子。则有:

\[C^M_N=\frac{N!}{M!(N-M)!}=\frac{N!\ div\ D}{(M!\ div\ D)\times ((N-M)!\ div\ D)}\times D^K \]

因此若上式分母与 \(D\) 互质,则

\[C^M_N\ dmod\ D=(N!\ div\ D)\times inv(M!\ div\ D)\times inv((N-M)!\ div\ D)\mod{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\) 为质因子。于是可以推出:

\[C_M^N=\frac{N!\ div\ p_i}{(M!\ div\ p_i)\times ((N-M)!\ div\ p_i)}\times p_i^{k_i} \]

因此我们可以得到 \(K=\underset{i\in[1,s]}{\min{\frac{k_i}{a_i}}}\) ,然后推出:

\[C^M_N\ div\ D\equiv \frac{C^M_N}{D^K}\equiv \frac{(C^M_N\ div\ p_i)\times p_i^{k_i}}{D^K}\equiv\frac{(C^M_N\ div\ p_i)\times p_i^{k_i-Ka_i}}{(\frac{D}{D_i})^K}\mod{D_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!=\underset{D\mid i}{\prod^N_{i=1}}{i}\times \underset{D\nmid i}{\prod^N_{i=1}}{i}=(D^{\lfloor \frac{N}{D}\rfloor}\times \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})) \]

因此:\(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;
}
posted @ 2020-04-14 22:44  st1vdy  阅读(2450)  评论(0编辑  收藏  举报