Atcoder abc441A~F 题解

T1

题目大意

有一个 1010010^{100}10100 行 1010010^{100}10100 列的网格.

输入 P,Q,X,YP, Q, X, YP,Q,X,Y, 首先你需要将 (P,Q)(P, Q)(P,Q) 为左上角的 100×100100\times 100100×100 的正方形包括的位置染成黑色. 判断 (X,Y)(X, Y)(X,Y) 是否被染成了黑色.

思路

区间判断即可.

当 P≤X<P+100 and Q≤Y<Q+100P\leq X < P + 100 \text{ and } Q\leq Y < Q + 100P≤X<P+100 and Q≤Y<Q+100 时输出 Yes 即可.

cpp 复制代码
int main()
{
	int X, Y, P, Q;
	scanf("%d %d %d %d", &P, &Q, &X, &Y);
	
	if (P <= X && X <= P + 99 && Q <= Y && Y <= Q + 99) puts("Yes");
	else puts("No");
	return 0;
}

T2

题目大意

有两种语言, 每个语言都有一组特定的小写字母, 有 QQQ 个询问, 让你判断输入的字符串是哪种语言, 两种都可能是或两种都不是输出 Unknown.

思路

模拟判断即可.

cpp 复制代码
int N, M;
char str1[128], str2[128];
int vis1[256], vis2[256];

int main()
{
	scanf("%d %d\n", &N, &M);
	scanf("%s", str1);
	scanf("%s", str2);
	
	for (int i = 0; i < N; i++) vis1[(int) str1[i]] = 1;
	for (int i = 0; i < M; i++) vis2[(int) str2[i]] = 1;
	
	int Q;
	scanf("%d", &Q);
	while (Q--) {
		char q[128] = {0};
		scanf("%s", q);
		
		int flag1 = 1, flag2 = 1;
		int len = strlen(q);
	
		for (int i = 0; i < len; i++) {
			if (!vis1[(int) q[i]]) flag1 = 0;
			if (!vis2[(int) q[i]]) flag2 = 0;
		}
		
		if (flag1 && !flag2) printf("Takahashi\n");
		else if (!flag1 && flag2) printf("Aoki\n");
		else printf("Unknown\n");
	}
	return 0;
}

T3

题目大意

有 NNN 个瓶子, 每个瓶子里面有 AiA_iAi mL 液体.

瓶子里可能出现两种液体: 水和烧酒.

现在我们不知道瓶子里是什么液体, 但是我们知道刚好有 KKK 瓶是烧酒, 问在最坏的情况, 你至少 要喝多少瓶才能喝到 XXX mL 烧酒.

思路

考虑到最坏的情况, 这 KKK 瓶烧酒刚好就是最少的瓶子里面的, 我们要选的最少, 自然是每次选择最大的一瓶烧酒, 按照题意排序模拟即可, 时间复杂度 O(Nlog⁡N).O(N\log N).O(NlogN).

cpp 复制代码
#define MAXN 300010
long long a[MAXN];

long long q = 0;
int main()
{
	int n, k;
	scanf("%d %d %lld", &n, &k, &q);
	for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
	
	sort(a + 1, a + n + 1);
	long long sum = 0;
	for (int i = 1; i <= k; i++) sum += a[i];
	
	if (sum < q) return printf("-1"), 0;
	
	sum = 0;
	int ans = n - k;
	
	for (int i = k; i >= 1; i--) {
		sum += a[i];
		ans++;
		if (sum >= q) return printf("%d", ans), 0;
	}
//	printf("%d", ans);
	return 0;
}

T4

题目大意

有一个 NNN 和节点 MMM 条边的有向图, 每个边都有一个权值 CiC_iCi, 现在我们知道, 这个图的每个点的出边最多为 444, 我们现在要求满足这样条件的节点:

  • 这个点可以从节点 111 走到, 而且路径长度要刚好为 LLL.(可以走重复的点和边.)
  • 这条路径上面的点权 CiC_iCi 之和要在 [S,T][S, T][S,T] 这个区间里面.

形式化的讲:

有一个 NNN 个节点 MMM 条边的有向图, 每个边连接点 Ui,ViU_i,V_iUi,Vi 权值为 CiC_iCi, 我们现在要找这样的节点:(设这个节点为 PPP)

  • 设从节点 111 到节点 PPP 存在长度为 LLL 的路径.
  • 这些路径中的任意一条, 我们设这条路径上面的点为 N1,N2,...,NLN_1,N_2,...,N_LN1,N2,...,NL(Np=NqN_p=N_qNp=Nq是允许的). 如果 ∑i=1LNi∈[S,T]\sum_{i=1}^{L}N_i\in[S,T]∑i=1LNi∈[S,T], 那么这个点就是合法的.

思路

因为 L≤10L\leq 10L≤10, 所以我们可以直接暴搜.

因为每个点的出边最多为 444, 410≈1064^{10}\approx10^6410≈106 不会超时.

cpp 复制代码
int N, M, L, S, T;
const int MAXN = 2e5 + 10;

set<int> st;
vector<pair<int, int>> G[MAXN];
int vis[MAXN];

void dfs(int u, int sum, int l) {
	if (l == L) {
		if (S <= sum && sum <= T) st.insert(u);
		return ; 
	}
	for (auto [v, w] : G[u]) dfs(v, sum + w, l + 1);
	
}

int main()
{
	scanf("%d %d %d %d %d", &N, &M, &L, &S, &T);
	for (int i = 1; i <= M; i++) {
		int x, y, z;
		scanf("%d %d %d", &x, &y, &z);
		G[x].push_back({y, z});
	}
	
	dfs(1, 0, 0);
	for (int it : st) printf("%d ", it);
	return 0;
}

T5

题目描述

有一个长度为 NNN 的字符串 SSS, 我们要统计满足一下条件的子串个数:

设这个子串 A 的个数为 PPP, B 的个数为 QQQ. 如果这个字符串满足 P>QP>QP>Q, 那么就满足条件.

思路

这里 NNN 是 5×1055\times 10^55×105 的级别. 我们好好考虑该怎么解决这个问题.

我们使用两个前缀和数组 ai,bia_i,b_iai,bi.

  • aia_iai 表示从 111 到 iii 中 A 的个数.
  • bib_ibi 表示从 111 到 iii 中 B 的个数.

那么子串 Sl,Sl+1,...,SrS_l,S_{l+1},...,S_rSl,Sl+1,...,Sr 的 AB 的个数分别为:

  • A 的个数就是 ar−al−1a_r-a_{l-1}ar−al−1.
  • B 的个数就是 br−bl−1b_r-b_{l-1}br−bl−1.

我们发现满足条件的字符串其实就是:
ar−al−1>br−bl−1a_r-a_{l-1}>b_r-b_{l-1}ar−al−1>br−bl−1

移一下项
ar−br>al−1−bl−1a_r-b_r>a_{l-1}-b_{l-1}ar−br>al−1−bl−1

发现统计这个可以使用树状数组, 边操作边统计答案, 时间复杂度为 O(Nlog⁡N)O(N\log N)O(NlogN)

注意减法可能出现负数, 可以考虑加上一个大的常数.

cpp 复制代码
const int MAXN = 5e5 + 10;

int N;
char str[MAXN];

// 树状数组
int sum[MAXN << 3];
void add(int pos, int val) {
	for (; pos <= (MAXN << 1); pos += pos & -pos) sum[pos] += val;
}
int get_sum(int pos) {
	int ret = 0;
	for (; pos > 1; pos -= pos & -pos) ret += sum[pos];
	return ret;
}

int a[MAXN], b[MAXN];

int main()
{
	scanf("%d", &N);
	scanf("%s", str + 1);
	
	for (int i = 1; i <= N; i++) {
		a[i] = a[i - 1] + (str[i] == 'A');
		b[i] = b[i - 1] + (str[i] == 'B');
	}
	
	long long ans = 0;
	for (int i = 1; i <= N; i++) {
		int id = a[i] - b[i] + MAXN;
		add(a[i - 1] - b[i - 1] + MAXN, 1);
		ans += get_sum(id - 1);
	}
	
	printf("%lld\n", ans);
	
	return 0;
}

T6

赛时差一点就想出来了.

题目描述

这里不想翻译了, 可以自己查看

思路

考虑分析如何使用 dp 解决这个问题.

我们设 dp1i,jdp1_{i,j}dp1i,j 表示考虑前 iii 个物品, 总重量最大为 jjj 时的最大价值.

再设 dp2i,jdp2_{i,j}dp2i,j 表示考虑第 iii 个物品到第 NNN 个物品, 总重量最大为 jjj 时的最大价值.

这两个数组就是 0/1 背包, 时间复杂度为 O(NM)O(NM)O(NM)

那么最大价值一定就是 dp1N,Mdp1_{N,M}dp1N,M, 我们设这个为 SSS.

我们一个一个考虑: (设当前的物品编号为 iii)

对于 A 的情况.

我们发现我们可以考虑不买这个东西, 看看是不是对于所有的 jjj, 都得不到最优的解

那么在这里, 不考虑这个东西的价值就是 dp1i−1,j+dp2i+1,M−jdp1_{i-1,j}+dp2_{i+1,M-j}dp1i−1,j+dp2i+1,M−j.
对于 C 的情况.

我们可以考虑买下这个东西, 看看是不是能得到最优解.

那么买了这个东西的总重量就变成了 M−PiM-P_iM−Pi, 同时我们也要增加 ViV_iVi.

那么我们再使用 dp 数组: 价值就是 dp1i−1,j+dp2i+1,M−Pi−j+Vidp1_{i-1,j}+dp2_{i+1,M-P_i-j}+V_idp1i−1,j+dp2i+1,M−Pi−j+Vi.
那么剩下的情况就是 B 了.

这个步骤需要枚举 i,ji,ji,j 时间复杂度为 O(NM)O(NM)O(NM) 前一个步骤也是 O(NM)O(NM)O(NM).

所以总的时间复杂度为 O(NM)O(NM)O(NM) 空间复杂度为 O(NM)O(NM)O(NM).

cpp 复制代码
typedef long long LL;

const int MAXN = 1024;
const int MAXM = 5e4 + 10;
LL dp1[MAXN][MAXM], dp2[MAXN][MAXM];

int N, M;
int P[MAXN], V[MAXN];

template <class T> inline T max(const T& x, const T& y) {return x > y ? x : y;}
template <class T> inline T min(const T& x, const T& y) {return x < y ? x : y;}

signed main()
{
	scanf("%d %d", &N, &M);
	for (int i = 1; i <= N; i++) scanf("%d %d", &P[i], &V[i]);
	
	for (int i = 1; i <= N; i++)
		for (int j = 0; j <= M; j++) {
			dp1[i][j] = dp1[i - 1][j];
			if (j >= P[i]) dp1[i][j] = max(dp1[i][j], dp1[i - 1][j - P[i]] + V[i]);
		}
		
	for (int i = N; i >= 1; i--) {
		for (int j = M; j >= 1; j--) {
			dp2[i][j] = dp2[i + 1][j];
			if (j >= P[i]) dp2[i][j] = max(dp2[i][j], dp2[i + 1][j - P[i]] + V[i]);
		}
		
	}
	
	LL ans = dp1[N][M];
	
	for (int i = 1; i <= N; i++) {
		// 考虑是否为 C 类物品
		int isC = 1;
		LL W = M - P[i];
		LL cmp = ans - V[i];
		
		for (int j = 0; j <= W; j++) if (dp1[i - 1][j] + dp2[i + 1][W - j] >= cmp) {isC = 0; break;}
		if (isC) {putchar('C'); continue;}
		
		int isA = 1;
		for (int j = 0; j <= M; j++) if (dp1[i - 1][j] + dp2[i + 1][M - j] == ans) {isA = 0; break;}
		if (isA) putchar('A');
		else putchar('B');
	}
	return 0;
}
相关推荐
少林码僧2 小时前
2.30 传统行业预测神器:为什么GBDT系列算法在企业中最受欢迎
开发语言·人工智能·算法·机器学习·ai·数据分析
豆沙沙包?2 小时前
2026年--Lc343-1926. 迷宫中离入口最近的出口(图 - 广度优先搜索)--java版
java·算法·宽度优先
超级大福宝3 小时前
【力扣200. 岛屿数量】的一种错误解法(BFS)
数据结构·c++·算法·leetcode·广度优先
独自破碎E3 小时前
【动态规划=递归+记忆化存储】跳台阶
算法·动态规划
一颗青果3 小时前
auto | 尾置返回类型 | decltype | using | typedef
java·开发语言·算法
郝学胜-神的一滴3 小时前
何友院士《人工智能发展前沿》全景解读:从理论基石到产业变革
人工智能·python·深度学习·算法·机器学习
BHXDML3 小时前
第五章:支持向量机
算法·机器学习·支持向量机
2401_841495643 小时前
具身智能:从理论到现实,人工智能的下一场革命
人工智能·算法·机器人·硬件·具身智能·通用智能·专用智能
Felven3 小时前
B. MEXor Mixup
算法