ARC 刷题记录 · 壹 | 错题本

标黄的表示看了题解。

[ARC058B] Iroha and a Grid

简单计数题,总方案减不合法的即可,不合法的可以枚举 A × B A \times B A×B 的矩形的一个对角线计算。

#include <bits/stdc++.h>

const int MAXN = 200000;
const int MOD = 1000000007;

int H, W, A, B;

inline int Add(const int &x, const int &y) {
	return (x + y >= MOD) ? (x + y - MOD) : (x + y);
}

inline int Mul(const int &x, const int &y) {
	return (long long)x * y % MOD;
}

int Pow(int x, int y) {
	int ret = 1;
	while (y) {
		if (y & 1)
			ret = Mul(ret, x);
		x = Mul(x, x);
		y >>= 1;
	}
	return ret;
}

int Fac[MAXN + 5], Inv[MAXN + 5];

inline int Calc(const int &n, const int &m) {
	return Mul(Fac[n + m - 2], Mul(Inv[n - 1], Inv[m - 1]));
}

int main() {
	scanf("%d%d%d%d", &H, &W, &A, &B);
	int N = H + W;
	Fac[0] = 1;
	for (int i = 1; i <= N; i++)
		Fac[i] = Mul(Fac[i - 1], i);
	Inv[N] = Pow(Fac[N], MOD - 2);
	for (int i = N - 1; i >= 0; i--)
		Inv[i] = Mul(Inv[i + 1], i + 1);
	int X = Calc(H, W), Y = 0;
	for (int i = H - A + 1; i <= std::min(H, H - A + B); i++) {
		int j = B - (i - (H - A)) + 1;
		Y = Add(Y, Mul(Calc(i, j), Calc(H - i + 1, W - j + 1)));
	}
	printf("%d", (X - Y + MOD) % MOD);
	return 0;
}

[ARC058C] Iroha and Haiku

用二进制第 i i i 1 1 1 的数来表示数 i i i,例如 4 → 1000 4 \to 1000 41000 2 → 10 2 \to 10 210,那么数相加可以把这些数对应的转化过后的数依次排列起来就能看出后缀和是多少了。那么这样处理过后原问题的合法定义变为存在四个 1 1 1(最末尾视为有一个 1 1 1),相邻两个 1 1 1 之间的距离分别为 X , Y , Z X, Y, Z X,Y,Z(距离定义为两个 1 1 1 位置之差)。反向考虑比较简单,即不存这样的情况,状压 DP 即可。

#include <bits/stdc++.h>

const int MAXN = 40;
const int MAXL = 17;
const int MOD = 1000000007;

inline void AddTo(int &x, const int &y) {
	x += y; if (x >= MOD) x -= MOD;
}

int N, X, Y, Z;
int Dp[MAXN + 5][(1 << MAXL) + 5];

int main() {
	scanf("%d%d%d%d", &N, &X, &Y, &Z);
	int Ans = 0, M = (1 << (X + Y + Z)) - 1;
	int Bad = (1 << (Z - 1)) | (1 << (Z + Y - 1)) | (1 << (Z + Y + X - 1));
	Dp[0][0] = 1;
	for (int i = 0; i < N; i++)
		for (int j = 0; j <= M; j++)
			if (Dp[i][j]) {
				for (int k = 1; k <= 10; k++) {
					int p = ((j << k) | (1 << (k - 1))) & M;
					if ((p & Bad) != Bad)
						AddTo(Dp[i + 1][p], Dp[i][j]);
				}
			}
	for (int i = 0; i <= M; i++)
		AddTo(Ans, Dp[N][i]);
	int All = 1;
	for (int i = 1; i <= N; i++)
		All = (long long)All * 10 % MOD;
	printf("%d", (All - Ans + MOD) % MOD);
	return 0;
}

[ARC059C] Children and Candies

要根据分配律全部一起计算,因此直接定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个人分了 j j j 个糖的答案,转移就显然了。看到多重方案数嵌套求和的不要想多了,一起算就行了。

#include <bits/stdc++.h>

const int MAXN = 400;
const int MOD = 1000000007;

inline int Add(const int &x, const int &y) {
	return (x + y >= MOD) ? (x + y - MOD) : (x + y);
}

inline int Mul(const int &x, const int &y) {
	return (long long)x * y % MOD;
}

inline int Sub(const int &x, const int &y) {
	return (x - y < 0) ? (x - y + MOD) : (x - y);
}

int Pow(int x, int y) {
	int ret = 1;
	while (y) {
		if (y & 1)
			ret = Mul(ret, x);
		y >>= 1;
		x = Mul(x, x);
	}
	return ret;
}

int N, C;
int A[MAXN + 5], B[MAXN + 5];
int S[MAXN + 5][MAXN + 5], Dp[MAXN + 5][MAXN + 5];

int main() {
	scanf("%d%d", &N, &C);
	int Max = 0;
	for (int i = 1; i <= N; i++)
		scanf("%d", &A[i]);
	for (int i = 1; i <= N; i++)
		scanf("%d", &B[i]), Max = std::max(Max, B[i]);
	for (int i = 0; i <= C; i++)
		for (int j = 1; j <= Max; j++)
			S[i][j] = Add(S[i][j - 1], Pow(j, i));
	Dp[0][0] = 1;
	for (int i = 1; i <= N; i++)
		for (int j = 0; j <= C; j++)
			for (int k = 0; k <= j; k++)
				Dp[i][j] = Add(Dp[i][j], Mul(Dp[i - 1][j - k], Sub(S[k][B[i]], S[k][A[i] - 1])));
	printf("%d", Dp[N][C]);
	return 0;
}

[ARC059D] Unhappy Hacking

原串具体长什么样不重要,我们只需要求“ n n n 次按出长度为 l l l 的串的方案数”然后除以 2 l 2^l 2l 次方即可。(因为每种样子的串是等价的), d p [ i ] [ j ] dp[i][j] dp[i][j] 表示按 i i i 次按出长度为 j j j 的串的方案数,转移显然。

#include <bits/stdc++.h>

const int MAXN = 5000;
const int MOD = 1000000007;

inline int Add(const int &x, const int &y) {
	return (x + y >= MOD) ? (x + y - MOD) : (x + y);
}

inline int Mul(const int &x, const int &y) {
	return (long long)x * y % MOD;
}

inline int Sub(const int &x, const int &y) {
	return (x - y < 0) ? (x - y + MOD) : (x - y);
}

int Pow(int x, int y) {
	int ret = 1;
	while (y) {
		if (y & 1)
			ret = Mul(ret, x);
		y >>= 1;
		x = Mul(x, x);
	}
	return ret;
}

int N, L;
int Dp[MAXN + 5][MAXN + 5];
char S[MAXN + 5];

int main() {
	scanf("%d%s", &N, S + 1);
	L = strlen(S + 1);
	Dp[0][0] = 1;
	for (int i = 1; i <= N; i++) {
		Dp[i][0] = Dp[i - 1][0];
		for (int j = 0; j <= N; j++) {
			Dp[i][j] = Add(Dp[i][j], Dp[i - 1][j + 1]);
			Dp[i][j] = Add(Dp[i][j], Mul(Dp[i - 1][j - 1], 2));
		}
	}
	printf("%d", Mul(Dp[N][L], Pow(Pow(2, L), MOD - 2)));
	return 0;
}

[ARC060B] Digit Sum

b > n b > \sqrt n b>n n n n b b b 进制表示最多有 2 2 2,于是 b ≤ n b \leq \sqrt n bn 暴力, b > n b > \sqrt n b>n 时解个二元一次方程即可。注意特判 n = s n = s n=s(因为我们只考虑了 b ≤ n b \leq n bn 的情况)。

#include <bits/stdc++.h>

typedef long long LL;

LL N, S;

LL Sum(LL n, const LL &b) {
	LL ret = 0;
	while (n)
		ret += n % b, n /= b;
	return ret;
}

int main() {
	scanf("%lld%lld", &N, &S);
	for (int i = 2; (LL)i * i <= N; i++)
		if (Sum(N, i) == S)
			return printf("%d", i), 0;
	LL Ans = -1;
	// i = 0: j = N
	if (N == S) return printf("%lld", N + 1), 0;
	for (int i = 1; (LL)i * i < N && i <= S; i++) {
		// i * b + j = N (j < b)
		// i + j = S
		// i * (b - 1) = N - S
		// b = (N - S) / i + 1
		if ((N - S) % i == 0) {
			LL b = (N - S) / i + 1;
			if (S - i < b && b >= 2) {
				Ans = b;
//				printf("%d %d %d\n", i, S - i, b);
			}
		}
	}
//	printf("%lld\n", Sum(N, Ans));
	printf("%lld", Ans);
	return 0;
}

[ARC060C] Tak and Hotels

佛了呀倍增写不来 WA 了 5 发最后看了别人的代码才过……
要写 r < R[l][i] 最后 ans 加一,而不是 r >= R[l][i](这样边界有问题)。

#include <bits/stdc++.h>

const int MAXN = 100000;
const int LOG = 16;

int N, L, Q;
int X[MAXN + 5];
int R[MAXN + 5][LOG + 5];

int main() {
	scanf("%d", &N);
	for (int i = 1; i <= N; i++)
		scanf("%d", &X[i]);
	scanf("%d%d", &L, &Q);
	for (int i = 1; i <= N; i++)
		R[i][0] = std::upper_bound(X + i + 1, X + N + 1, X[i] + L) - X - 1;
	for (int j = 1; j <= LOG; j++)
		for (int i = 1; i <= N; i++)
			R[i][j] = R[R[i][j - 1]][j - 1];
	while (Q--) {
		int l, r; scanf("%d%d", &l, &r);
		if (l > r) std::swap(l, r);
		int Ans = 0;
		for (int i = LOG; i >= 0; i--)
			if (r > R[l][i])
				l = R[l][i], Ans |= (1 << i);
		printf("%d\n", Ans + 1);
	}
	return 0;
}

[ARC060D] Best Representation

KMP 判循环:字符串 s s s 为循环串当且仅当 ( ∣ s ∣ − f a i l ∣ s ∣ ) ∣ ∣ s ∣ (|s|-fail_{|s|}) \big| |s| (sfails)s,此时 ∣ s ∣ − f a i l ∣ s ∣ |s| - fail_{|s|} sfails 是最小循环节的长度。然后分三类讨论一下即可。

#include <bits/stdc++.h>

const int MAXN = 500000;

int N;
char S[MAXN + 5];
int Fail1[MAXN + 5], Fail2[MAXN + 5];

void KMP(int *res) {
	res[1] = 0;
	for (int i = 2, j = 0; i <= N; i++) {
		while (j && S[i] != S[j + 1])
			j = res[j];
		if (S[j + 1] == S[i])
			j++;
		res[i] = j;
	}
}

bool Cir(int *Fail, int i) {
	return Fail[i] && i % (i - Fail[i]) == 0;
}

int main() {
	scanf("%s", S + 1); N = strlen(S + 1);
	KMP(Fail1), std::reverse(S + 1, S + N + 1), KMP(Fail2);
	if (Fail1[N] && N % (N - Fail1[N]) == 0) {
		if (Fail1[N] == N - 1)
			return printf("%d\n1", N), 0;
		puts("2");
		int Ans = 0;
		for (int i = 1; i < N; i++)
			if (!Cir(Fail1, i) && !Cir(Fail2, N - i))
				Ans++;
		printf("%d\n", Ans);
		return 0;
	}
	puts("1\n1");
	return 0;
}

[ARC061C] Snuke’s Subway Trip

对点 u u u 拆为多个 u c u_c uc 表示在 u u u 处颜色为 c c c,连边 ( u , u c ) (u, u_c) (u,uc) 代价为 1 1 1 u c , u u_c, u uc,u 代价为 0 0 0 可实现颜色转换的代价,原图边按 ( u c , v c ) (u_c, v_c) (uc,vc) 代价为 0 0 0 (其中 c c c ( u , v ) (u, v) (u,v) 的颜色)连即可。

#include <bits/stdc++.h>

typedef std::pair<int, int> PII;

const int MAXN = 100000;
const int MAXM = 200000;
const int MAXC = 1000000;

struct Edge {
	int v, w;
	Edge *nxt;
} E[MAXM * 10 + 5];
Edge *Adj[MAXM * 5 + 5], *EdgeCnt = E;

int N, M;
int U[MAXM + 5], V[MAXM + 5], C[MAXM + 5];

void AddEdge(int u, int v, int w) {
	(++EdgeCnt)->v = v, EdgeCnt->w = w, EdgeCnt->nxt = Adj[u], Adj[u] = EdgeCnt;
}

bool Vis[MAXC + 5];
std::map<PII, int> Num;
std::vector<int> Col[MAXN + 5];

int Dist[MAXM * 5 + 5];

int Dijkstra(int S, int T) {
	std::priority_queue<PII, std::vector<PII>, std::greater<PII> > Q;
	memset(Dist, 0x3f, sizeof Dist);
	Q.push({ Dist[S] = 0, S });
	while (!Q.empty()) {
		int u = Q.top().second, d = Q.top().first; Q.pop();
		if (d > Dist[u]) continue;
		for (Edge *i = Adj[u]; i; i = i->nxt) {
			int v = i->v, w = i->w;
			if (Dist[v] > d + w)
				Q.push({ Dist[v] = d + w, v });
		}
	}
	return Dist[T] == 0x3f3f3f3f ? -1 : Dist[T];
}

int main() {
	scanf("%d%d", &N, &M);
	for (int i = 1; i <= M; i++) {
		scanf("%d%d%d", &U[i], &V[i], &C[i]);
		Col[U[i]].push_back(C[i]), Col[V[i]].push_back(C[i]);
	}
	int cnt = 0;
	for (int i = 1; i <= N; i++) {
		for (int j: Col[i])
			if (!Vis[j]) {
				int u = (Num[{ i, j }] = N + ++cnt);
				Vis[j] = true;
				AddEdge(i, u, 1);
				AddEdge(u, i, 0);
			}
		for (int j: Col[i])
			Vis[j] = false;
	}
	for (int i = 1; i <= M; i++) {
		int u = Num[{ U[i], C[i] }], v = Num[{ V[i], C[i] }];
		AddEdge(u, v, 0);
		AddEdge(v, u, 0);
	}
	printf("%d", Dijkstra(1, N));
	return 0;
}

[ARC063C] Integers on a Tree

优先队列维护一下,每次取出最小权值结点扩展其周围的未确定结点的权值即可,因为必然存在一种方案:未确定的点权值不比全场最小的还小。

#include <bits/stdc++.h>

const int MAXN = 100000;

int N, K;
std::vector<int> G[MAXN + 5];

int Ans[MAXN + 5];
struct Node {
	int i, v;

	inline bool operator < (const Node &other) const {
		return v > other.v;
	}
};
std::priority_queue<Node> Q;

int main() {
	scanf("%d", &N);
	memset(Ans, -1, sizeof Ans);
	for (int i = 1; i < N; i++) {
		int u, v; scanf("%d%d", &u, &v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	scanf("%d", &K);
	for (int i = 1; i <= K; i++) {
		Node cur; scanf("%d%d", &cur.i, &cur.v);
		Q.push(cur), Ans[cur.i] = cur.v;
	}
	while (!Q.empty()) {
		int u = Q.top().i; Q.pop();
		for (int i = 0; i < (int)G[u].size(); i++) {
			int v = G[u][i];
			if (Ans[v] == -1)
				Q.push({ v, Ans[v] = Ans[u] + 1});
			else if (Ans[v] != Ans[u] + 1 && Ans[v] != Ans[u] - 1)
				return puts("No"), 0;
		}
	}
	puts("Yes");
	for (int i = 1; i <= N; i++)
		printf("%d\n", Ans[i]);
	return 0;
}

[ARC064C] Cosmic Rays

N ≤ 1000 N \leq 1000 N1000,暴力最短路即可。

#include <bits/stdc++.h>

typedef std::pair<double, int> PDI;

//const double EPS = 1e-9;
const int MAXN = 1002;
const int MAXM = MAXN * (MAXN - 1);

int N, K;
struct Edge {
	int v; double w; Edge *nxt;
} E[MAXM + 5];
Edge *Adj[MAXN + 5], *EdgeCnt = E;

inline void AddEdge(const int &u, const int &v, const double &w) {
	(++EdgeCnt)->v = v, EdgeCnt->w = w, EdgeCnt->nxt = Adj[u], Adj[u] = EdgeCnt;
	(++EdgeCnt)->v = u, EdgeCnt->w = w, EdgeCnt->nxt = Adj[v], Adj[v] = EdgeCnt;
}

struct Circle {
	int x, y, r;
} B[MAXN + 5];

inline double Out(const Circle &i, const Circle &j) {
	return sqrt((double)(i.x - j.x) * (i.x - j.x) + (double)(i.y - j.y) * (i.y - j.y)) - i.r - j.r;
}

double Dist[MAXN + 5];

double Dijkstra(const int &S, const int &T) {
	std::priority_queue<PDI, std::vector<PDI>, std::greater<PDI> > Q;
	for (int i = 1; i <= N; i++)
		Dist[i] = 1e18;
	Q.push({ Dist[S] = 0, S });
	while (!Q.empty()) {
		double d = Q.top().first; int u = Q.top().second; Q.pop();
		if (d - Dist[u] > 1e-9) continue;
		for (Edge *i = Adj[u]; i; i = i->nxt) {
			int v = i->v;
			if (Dist[v] > d + i->w)
				Q.push({ Dist[v] = d + i->w, v});
		}
 	}
 	return Dist[T];
}

int main() {
	scanf("%d%d%d%d", &B[1].x, &B[1].y, &B[2].x, &B[2].y);
	scanf("%d", &N), N += 2;
	for (int i = 3; i <= N; i++)
		scanf("%d%d%d", &B[i].x, &B[i].y, &B[i].r);
	for (int i = 1; i <= N; i++)
		for (int j = i + 1; j <= N; j++)
			AddEdge(i, j, std::max(Out(B[i], B[j]), 0.0));
	printf("%.10f", Dijkstra(1, 2));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值