2018-2019 ACM-ICPC, Asia Dhaka Regional Contest

Contest Info


[Practice Link]()
Solved A B C D E F G H I J
7/10 - O O - O O O Ø - O
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


B. Counting Inversion

题意:
计算\([l, r]\)所有数的数位表示中有多少逆序对。

代码:

view code
#include <bits/stdc++.h>

using namespace std;

#define dbg(x...) do { cout << "\033[32;1m" << #x << " -> "; err(x); } while (0)
void err() { cout << "\033[39;0m" << endl;}
template<class T, class... Ts> void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...); }


using ll = long long;

const int N = 20;

struct node {
	ll sum, num;
	ll cnt[N];

	node() {
		sum = num = 0;
		memset(cnt, 0, sizeof cnt);
	}
}dp[N];

int vis[N];
int a[N];

node gogogo(int pos, int st, int limit) {
	if (!limit && vis[pos]) return dp[pos];
	if (pos == 1) {
		node res = node();
		res.num = 1;
		return res;
	}
	node res = node();
	int up = limit ? a[pos - 1] : 9;
	for (int i = 0; i <= up; ++i) {
		node other = gogogo(pos - 1, i, limit && i == up);
		res.sum += other.sum;
		for (int j = i + 1; j <= 9; ++j) res.sum += other.cnt[j];
		for (int j = 0; j <= 9; ++j) res.cnt[j] += other.cnt[j];
		res.cnt[i] += other.num;
		res.num += other.num;
	}
	vis[pos] = 1;
	dp[pos] = res;
	return res;
}

ll gao(ll x) {
	if (x < 10) return 0;
	for (int i = 0; i < N; ++i) dp[i] = node();
	memset(vis, 0, sizeof vis);
	ll res = 0;
	*a = 0;
	ll tmp = x;
	while (tmp) {
		a[++*a] = tmp % 10;
		tmp /= 10;
	}
	for (int i = 1; i <= *a; ++i) {
		int up = (i == *a ? a[i] : 9);
		for (int j = 1; j <= up; ++j) {
			node other = gogogo(i, j, i == *a && j == a[i]);
			res += other.sum;
			for (int k = j + 1; k <= 9; ++k) {
				res += other.cnt[k];
			}
		//	dbg(i, j, res);
		}
	}
	return res;
}

int main() {
	int T;
	scanf("%d", &T);
	for (int cas = 1; cas <= T; ++cas) {
		ll l, r;
		scanf("%lld %lld", &l ,&r);
		printf("Case %d: %lld\n", cas, gao(r) - gao(l - 1));
	}
	return 0;
}

C. Divisors of the Divisors of An Integer

题意:
给出\(N\),问\(N!\)的因子的因子的个数和。

思路:
考虑\(N\)的唯一分解为\(p_1^{t_1}p_2^{t_2} \cdots p_k^{t_k}\),那么其因子个数为:

\[\begin{eqnarray*} \prod\limits_{i = 1}^k (t_i + 1) \end{eqnarray*} \]

我们单独考虑每种素数,假设它为\(p\),它的幂次为\(t\),那么它的因子的该素数的幂次的选择有\(0, 1, \cdots, t\)\(t + 1\)种选择
那么我们考虑因子的因子,有\((0), (0, 1), \cdots, (0, 1, \cdots, t)\)一共\((t + 1)(t + 2) / 2\)种选择。
所以我们将阶乘唯一分解即可。

代码:

view code
#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const ll p = 1e7 + 7;
const int N = 1e6 + 10;

int check[N];
int prime[N];

void Init() {
	check[1] = 1;
	*prime = 0;
	for (int i = 2; i < N; ++i) {
		if (!check[i]) {
			prime[++*prime] = i;
		}
		for (int j = 1; j <= *prime; ++j) {
			if (i * prime[j] >= N) break;
			check[i * prime[j]] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}

int n;

ll f(ll x) {
	ll res = 0;
	ll tmp = n;
	while (tmp) {
		res += tmp / x;
		tmp /= x;
	}
	return res;
}

int main() {
	Init();
	while (scanf("%d", &n) != EOF && n) {
		ll res = 1;
		for (int i = 1; i <= *prime && prime[i] <= n; ++i) {
			ll tmp = f(prime[i]);
			tmp++;
			tmp = tmp * (tmp + 1) / 2;
			tmp %= p;
			res = res * tmp % p;
		}
		printf("%lld\n", res);
	}
	return 0;
}

E. Helping the HR

按题意模拟即可。

代码:

view code
#include <bits/stdc++.h>

using namespace std;

int main() {
	int n;
	scanf("%d", &n);
	while (n != 0) {
		int h1, h2, m1, m2, s1, s2, ans = 0;
		int t1, t2;
		char ch;
		for (int i = 1; i <= n; ++i) {
			scanf(" %c:%d:%d:%d:%d:%d:%d", &ch, &h1, &m1, &s1, &h2, &m2, &s2);
			t1 = h1 * 3600 + m1 * 60 + s1;
			t2 = h2 * 3600 + m2 * 60 + s2;
			if (t2 < t1) t2 = 24 * 3600;
			if (t1 < 8 * 3600 + 30 * 60) t1 = 8 * 3600 + 30 * 60;
			if (ch == 'D' && t1 > 9 * 3600 + 30 * 60) ans++;
			else if (ch == 'E' && t1 > 12 * 3600 + 30 * 60) ans++;
			else if (ch == 'D' && t2 - t1 < 8 * 3600) ans++;
			else if (ch == 'E' && t2 - t1 < 9 * 3600) ans++;
		}
		if (ans == 0) printf("All OK\n");
		else if (ans > 3) printf("Issue Show Cause Letter\n");
		else printf("%d Point(s) Deducted\n", ans);
		scanf("%d", &n);
	}
	return 0;
}

F. Path Intersection

题意:
给出一棵树,每次给出\(k\)条路径,询问这\(k\)条路径交的点数。

思路:
\(k - 1\)次树上路径交即可。

代码:

view code
#include <bits/stdc++.h>
using namespace std;
using pII = pair<int, int>;
#define fi first
#define se second
const int N = 1e5 + 10, M = 20;
vector <vector<int>> G;

int n, q, fa[N], dep[N];
struct LCA {
	int F[N << 1], P[N];
	struct RMQ {
		int Min[N << 1][M];
		int mm[N << 1], rmq[N << 1];
		void init(int n) {
			mm[0] = -1;
			for (int i = 1; i <= n; ++i) {
				mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
				Min[i][0] = i;
			}
			for (int j = 1; j <= mm[n]; ++j) {
				for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
					Min[i][j] = rmq[Min[i][j - 1]] < rmq[Min[i + (1 << (j - 1))][j - 1]] ? Min[i][j - 1] : Min[i + (1 << (j - 1))][j - 1];
				}
			}
		}
		int queryMin(int x, int y) {
			if (x > y) swap(x, y);
			int k = mm[y - x + 1];
			return rmq[Min[x][k]] <= rmq[Min[y - (1 << k) + 1][k]] ? Min[x][k] : Min[y - (1 << k) + 1][k];
		}	
	}st;
	void dfs(int u) {
		F[++*F] = u;
		st.rmq[*F] = dep[u];
		P[u] = *F;
		for (auto &v : G[u]) if (v != fa[u]) {
			fa[v] = u;
			dep[v] = dep[u] + 1;
			dfs(v);
			F[++*F] = u;
			st.rmq[*F] = dep[u];
		}
	}
	void init(int rt) {
		*F = 0;
		fa[rt] = rt; dep[rt] = 0;
		dfs(rt);
		st.init(2 * n - 1);
	}
	int query(int u, int v) { return F[st.queryMin(P[u], P[v])]; }
	int dis(int u, int v) { return dep[u] + dep[v] - 2 * dep[query(u, v)] + 1; }
}lca;

pII gao(pII x, pII y) {
	if (x.fi == -1 || y.fi == -1) return pII(-1, -1);
	int t[] = {lca.query(x.fi, y.fi), lca.query(x.fi, y.se), lca.query(x.se, y.fi), lca.query(x.se, y.se)};
	sort(t, t + 4, [&](int x, int y) { return dep[x] < dep[y]; });
	int d[] = {lca.query(x.fi, x.se), lca.query(y.fi, y.se)};
	if (dep[d[0]] > dep[d[1]]) swap(d[0], d[1]);
	if (dep[t[3]] >= dep[t[2]] && dep[t[2]] >= dep[d[1]]) {
		return pII(t[2], t[3]);
	} else {
		return pII(-1, -1);
	}
} 

int main() {
	int _T; scanf("%d", &_T);
	for (int kase = 1; kase <= _T; ++kase) {
		printf("Case %d:\n", kase);
		scanf("%d", &n);
		G.clear(); G.resize(n + 1);
		for (int i = 1, u, v; i < n; ++i) {
			scanf("%d%d", &u, &v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		lca.init(1);
		scanf("%d", &q);
		int k; pII x, y;
		while (q--) {
			scanf("%d%d%d", &k, &x.fi, &x.se);
			for (int i = 2; i <= k; ++i) {
				scanf("%d%d", &y.fi, &y.se);
				x = gao(x, y);
			}
			if (x.fi == -1) puts("0");
			else printf("%d\n", lca.dis(x.fi, x.se));
		}
	}
	return 0;
}

G. Techland

题意:
给出一棵树,每次询问给出一个点和若一段区间,询问这个点到这个区间上的点的最小距离是多少。

思路:
将距离拆成\(dis_u + dis_v - 2dis_{lca}\)
然后发现一堆点到一个点的最小距离是可以通过祖先链预处理出一个\(dp\)数组的,那么我们每\(\sqrt{n}\)块预处理一个,然后块内\(O(1)\)查询,边角用\(rmq\)进行\(O(1)\)查询
时间复杂度\(O(n\sqrt{n})\)

代码:

view code
#include <bits/stdc++.h>
using namespace std;
using pII = pair<int, int>;
#define fi first
#define se second
template <class T1, class T2> void chmin(T1 &x, T2 y) { if (x > y) x = y; }
const int N = 1e5 + 10, M = 20, S = 200, INF = 0x3f3f3f3f;
vector <vector<int>> G;
int n, q, fa[N], dep[N], d_pos[N], d_posl[N], d_posr[N]; pII a[N]; 
struct LCA {
	int F[N << 1], P[N];
	struct RMQ {
		int Min[N << 1][M];
		int mm[N << 1], rmq[N << 1];
		void init(int n) {
			mm[0] = -1;
			for (int i = 1; i <= n; ++i) {
				mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
				Min[i][0] = i;
			}
			for (int j = 1; j <= mm[n]; ++j) {
				for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
					Min[i][j] = rmq[Min[i][j - 1]] < rmq[Min[i + (1 << (j - 1))][j - 1]] ? Min[i][j - 1] : Min[i + (1 << (j - 1))][j - 1];
				}
			}
		}
		int queryMin(int x, int y) {
			if (x > y) swap(x, y);
			int k = mm[y - x + 1];
			return rmq[Min[x][k]] <= rmq[Min[y - (1 << k) + 1][k]] ? Min[x][k] : Min[y - (1 << k) + 1][k];
		}	
	}st;
	void dfs(int u) {
		F[++*F] = u;
		st.rmq[*F] = dep[u];
		P[u] = *F;
		for (auto &v : G[u]) if (v != fa[u]) {
			fa[v] = u;
			dep[v] = dep[u] + 1;
			dfs(v);
			F[++*F] = u;
			st.rmq[*F] = dep[u];
		}
	}
	void init(int rt) {
		*F = 0;
		fa[rt] = rt; dep[rt] = 0;
		dfs(rt);
		st.init(2 * n - 1);
	}
	int query(int u, int v) { return F[st.queryMin(P[u], P[v])]; }
	int dis(int u, int v) { return dep[u] + dep[v] - 2 * dep[query(u, v)]; }
}lca;

struct HLD {
	int f[N];  
	void init() { for (int i = 1; i <= n; ++i) f[i] = INF + INF; }
	void dfs(int u) {
		for (auto &v : G[u]) if (v != fa[u]) {
			dfs(v);
			chmin(f[u], f[v]);
		}
	}
	void dfs2(int u) {
		f[u] -= 2 * dep[u];
		chmin(f[u], f[fa[u]]);
		for (auto &v : G[u]) if (v != fa[u]) {
			dfs2(v);
		}
	}
	void up(int x) {
		chmin(f[x], dep[x]);
	}
	void gao() {
		dfs(1);
		dfs2(1); 
	}
}hld[310];

int force(int x, int l, int r) {
	int res = INF;
	for (int i = l; i <= r; ++i)
		chmin(res, lca.dis(x, i));
	return res;
}

int query(int x, int l, int r) {
	if (l == -1 || r == -1) return INF;
	int res = INF;
	if (d_pos[l] == d_pos[r]) chmin(res, force(x, l, r));
	else {
		chmin(res, force(x, l, d_posr[d_pos[l]]));
		for (int i = d_pos[l] + 1; i < d_pos[r]; ++i) chmin(res, hld[i].f[x] + dep[x]);
		chmin(res, force(x, d_posl[d_pos[r]], r));
	}
	return res;
}

int main() {
	for (int i = 1; i <= 100000; ++i) {
		d_pos[i] = (i - 1) / S + 1;
		if (i == 1 || d_pos[i] != d_pos[i - 1]) d_posl[d_pos[i]] = i;
		d_posr[d_pos[i]] = i; 
	}
	int _T; scanf("%d", &_T);
	for (int kase = 1; kase <= _T; ++kase) {
		printf("Case %d:\n", kase);
		for (int i = 1; i <= 100000; ++i) a[i] = pII(-1, -1);
		scanf("%d", &n); 
		G.clear(); G.resize(n + 1);
		for (int i = 1, u, v; i < n; ++i) {
			scanf("%d%d", &u, &v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		lca.init(1);
		for (int i = 1; i <= d_pos[n]; ++i) hld[i].init();
		for (int i = 1; i <= n; ++i) {
			hld[d_pos[i]].up(i);
		}
		for (int i = 1; i <= d_pos[n]; ++i) hld[i].gao();
		scanf("%d", &q);
		int op, l, r, x, m, p;
		while (q--) {
			scanf("%d", &op);
			if (op == 1) {
				scanf("%d%d%d", &x, &l, &r);
				a[x] = pII(l, r);
			} else if (op == 2) {
				scanf("%d", &x);
				a[x] = pII(-1, -1);
			} else {
				scanf("%d%d", &x, &m);
				int res = INF;
				for (int i = 1; i <= m; ++i) {
					scanf("%d", &p);
					chmin(res, query(x, a[p].fi, a[p].se));
				}
				if (res >= INF) res = -1;
				printf("%d\n", res);
			}
		}
	}
	return 0;
}

H. Tile Game

题意:
给出一个二维矩形,里面有的格子有小球,每个小球颜色不同,有的格子没有小球。
现在可以让这些小球往一个方向移动,每次都是一起移动并且移动到不能移动为止。
定义一个稳定的局面当且仅当让他们往下移动或者往左移动的时候没有一个小球可以移动。
现在给出一个稳定局面,询问通过移动可以达到多少个不同的稳定局面。
两个稳定局面不同定义为至少有一个格子的小球它们的颜色不一样。

思路:
考虑移动套路是固定的,上右下左。
那么可以发现每个格子通过一次移动都有一个后继格子,并且所有格子会形成若干个环,这个就是他们的周期。
所以稳定局面的周期就是所有格子的周期的最小公倍数。
但是因为最小公倍数很大,并且有取模,所以不能采用\(\frac{ab}{gcd(a, b)}\)的方式来求。
考虑最小公倍数的本质含义,单独考虑每种质因子,这个质因子的幂次肯定是所有数中幂次最高的。

代码:

view code
#include <bits/stdc++.h>
using namespace std;
#define dbg(x...) do { cout << "\033[32;1m" << #x << " -> "; err(x); } while (0)
void err() { cout << "\033[39;0m" << endl;}
template<class T, class... Ts> void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...); }
using ll = long long;
#define fi first
#define se second
const int N = 210, mod = 78294349;
ll qpow(ll base, ll n) {
	ll res = 1;
	while (n) {
		if (n & 1) res = res * base % mod;
		base = base * base % mod;
		n >>= 1;
	}
	return res;
}
int n, m, id, a[N][N], b[4][N][N], mp[N * N]; char s[N][N]; 
vector <vector<int>> G;
inline int f(int x, int y) { return (x - 1) * m + y; }  
void getid() {
	memset(a, -1, sizeof a);
	id = 0;
	for (int i = 1; i <= n; ++i) { 
		for (int j = 1; j <= m; ++j) { 
			if (s[i][j] == '#')
				a[i][j] = ++id;
		}
	}
}

//往上走
void up(int a[N][N], int b[N][N]) {
	for (int j = 1; j <= m; ++j) { 
		int id = 1;
		for (int i = 1; i <= n; ++i) {  
			if (a[i][j] != -1) {
				b[id][j] = a[i][j];
			   	++id;	
			}
		}
	}
}

void right(int a[N][N], int b[N][N]) {
	for (int i = 1; i <= n; ++i) {
		int id = m;
		for (int j = m; j >= 1; --j) {
			if (a[i][j] != -1) {
				b[i][id] = a[i][j];
				--id;
			}
		}
	}
}

void down(int a[N][N], int b[N][N]) {
	for (int j = 1; j <= m; ++j) {
		int id = n;
		for (int i = n; i >= 1; --i) {
			if (a[i][j] != -1) {
				b[id][j] = a[i][j];
				--id;
			}
		}
	}
}

void left(int a[N][N], int b[N][N]) {
	for (int i = 1; i <= n; ++i) {
		int id = 1;
		for (int j = 1; j <= m; ++j) {
			if (a[i][j] != -1) {
				b[i][id] = a[i][j];
				++id;
			}
		}
	}
}

void build(int b[N][N]) {
	memset(mp, -1, sizeof mp);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			if (b[i][j] != -1) {
				mp[b[i][j]] = f(i, j);
			}
		}
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			if (a[i][j] != -1) {
				G[f(i, j)].push_back(mp[a[i][j]]);
			}
		}
	}
}

int fa[N * N], sze[N * N];
int find(int x) { return fa[x] == 0 ? x : fa[x] = find(fa[x]); }
void merge(int x, int y) {
	x = find(x), y = find(y);
	if (x != y) {
		if (sze[x] > sze[y]) swap(x, y);
		fa[x] = y;
		sze[y] += sze[x]; 
	}
}


int main() {
	int _T; scanf("%d", &_T);
	for (int kase = 1; kase <= _T; ++kase) {
		printf("Case %d: ", kase);
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n * m; ++i) fa[i] = 0, sze[i] = 1;
		G.clear(); G.resize(n * m + 10);
		for (int i = 1; i <= n; ++i)
			scanf("%s", s[i] + 1);
		memset(b, -1, sizeof b);
		getid();
		up(a, b[0]);
		right(b[0], b[1]);
		down(b[1], b[2]);
		left(b[2], b[3]);
		build(b[3]);
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= m; ++j) {
				if (a[i][j] != -1) {
					int p = f(i, j);
					merge(p, G[p][0]);
				}
			}
		}
		map <int, int> fac; 
		for (int i = 1; i <= n; ++i) { 
			for (int j = 1; j <= m; ++j) { 
				if (a[i][j] != -1) {
					int p = f(i, j);
					if (fa[p] == 0) { 
						int len = sze[p];
						for (int k = 2; k * k <= len; ++k) {
							int c = 0;
							while (len % k == 0) {
								len /= k;
								++c;
							}
							fac[k] = max(fac[k], c);
						}
						if (len > 1) fac[len] = max(fac[len], 1);
					}
				}
			}
		}
		ll res = 1;
		for (auto &it : fac) res = res * qpow(it.fi, it.se) % mod;
		printf("%lld\n", res);
	}
	return 0;
}

J. VAT Man

题意:
签到。

posted @ 2019-10-23 08:48  Dup4  阅读(738)  评论(0编辑  收藏  举报