AtCoder Beginner Contest 449 T3,T4,T5,T6题解

C - Comfortable Distance

原题指引

运行时间限制: 2 秒 / 内存限制: 1024 MiB

分数 : 300 分

问题描述

给定由英文字母小写组成的长度为 N 的字符串 S。

请求满足以下所有条件的整数对 (i, j) 的个数。

  • 1≤i≤j≤N
  • S_i = S_j
  • L≤j−i≤R

限制

2≤N≤5×10^5
1≤L≤R≤N−1
N, L, R 为整数
S 为长度为 N 的英文字母小写组成的字符串

输入

输入以以下格式从标准输入提供。

N L R
S

输出

请输出答案。

输入样例1

6 2 4
aabcba

输出样例1

2

输入样例2

9 3 6
aaaaaaaaa

输出样例2

18

输入样例3

10 2 6
aabbccaabb

输出样例3

6

solve

官方题解

翻译后

考虑固定 j 来统计答案。如果能够求出满足 j−R ≤ i ≤ j−L 且 S_i = S_j 的 i 的个数就可以了。这可以通过对每个小写字母的出现次数进行前缀和预计算来得到。具体来说,记 A_{c,k} 为前 1,2,…,k 个字符中英文字母 c 的出现次数,那么所求的 i 的个数就是 A_{S_j, max(j−L,0)} − A_{S_j, max(j−R−1,0)}。

上面的解法时间复杂度为 O(σN),其中 σ 为字母种类数量,但也可以在 O(σN) 时间内解决。始终管理 j−R ≤ i ≤ j−L 范围内的 S_i 的频率数组,并按 j=1,2,...,N 的顺序边更新边计算。当 j 增加 1 时,需要统计的范围变化为 O(1)。因此,同时维护整个频率数组并更新,就可以完成计算。下方的实现例子也可以参考。

zyz的解答

对于此题,用前缀和处理各字母出现个数

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
long long n,l,r;
long long s[30][maxn];
char c;
long long ans;
int main()
{
	cin >> n >> l >> r;
	for (long long i=1;i<=n;i++)
	{
		cin >> c;
		for (long long j=0;j<26;j++) s[j][i]=s[j][i-1];
		s[c-'a'][i]=s[c-'a'][i-1]+1;
		long long L=max(i-r,(long long)1);
		long long R=i-l;
		if (R<1) continue;
		ans += s[c-'a'][R]-s[c-'a'][L-1];
	}
	cout << ans << endl;
	return 0;
}

AC记录

D - Make Target 2

原题指引

运行时间限制: 2 秒 / 内存限制: 1024 MiB

分数 : 425 分

问题描述

在二维坐标平面上,坐标为 (x,y) 的格点,如果 max(|x|,|y|) 为偶数则涂黑,为奇数则涂白。

请计算满足 L≤x≤R 且 D≤y≤U 的整数对 (x,y) 中,被涂成黑色的坐标点的数量。

限制

  • $-106$≤L≤R≤$106$
  • $-106$≤D≤U≤$106$
  • 输入的值都是整数

输入

输入以以下格式从标准输入提供。

L R D U

输出

请输出答案。

输入样例1

-4 3 1 3

输出样例1

10

如上图所示,所求的答案是 10。

输入样例2

-14 14 -14 14

输出样例2

449

solve

官方题解

翻译后

为了简洁地处理 max(∣x∣,∣y∣) 这个式子,对满足 ∣x∣>∣y∣ 的情况和 ∣x∣≤∣y∣ 的情况进行分类计数,最后将它们相加即可得到答案。

以下介绍一个计算 ∣x∣>∣y∣ 情况数量的例子。∣x∣≤∣y∣ 的数量可以同样方法计算。

当 ∣x∣>∣y∣ 时,max(∣x∣,∣y∣)=∣x∣。因此,对满足 L≤x≤R 且 x 为偶数的所有 x 进行全搜索,对于每一个 x,计算满足 ∣x∣>∣y∣ 且 D≤y≤U 的 y 的个数即可。由于 ∣x∣>∣y∣ 等价于 −∣x∣<y<∣x∣,当 x 固定时,被统计的 y 的范围为 max(−∣x∣+1,D)≤y≤min(∣x∣−1,U)。这个范围内的整数 y 的个数为 max(0,min(∣x∣−1,U)−max(−∣x∣+1,D)+1),可以对每个 x 在 O(1) 时间内计算。因此,∣x∣>∣y∣ 的数量可以视 X=R−L,在 O(X) 时间内计算。

注释:对于max(−∣x∣+1,D)≤y≤min(∣x∣−1,U),我们发现|x|要+1或-1,原因是我们可以通过此方法去掉一些由于在计算|y|时也会计算(即算重)的点(那么在算|y|时就不用+1-1了)

Code

提示:不开long long见祖宗

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
long long l,r,d,u;
long long ans;
int main()
{
	cin >> l >> r >> d >> u;
	for (long long x=l;x<=r;x++)
	{
		if (x%2==0)
		{
			long long D=max(d,-abs(x)+1);
			long long U=min(u,abs(x)-1);
			long long c=U-D+1;
			ans += max(c,(long long)0);
		}
		
	}
	for (long long y=d;y<=u;y++)
	{
		if (y%2==0)
		{
			long long L=max(l,-abs(y));
			long long R=min(r,abs(y));
			long long c=R-L+1;
			ans += max(c,(long long)0);
		}
	}
	cout << ans << endl;
	return 0;
}

AC记录

E - A += v

原题指引

运行时间限制: 2 秒 / 内存限制: 1024 MiB

分数 : 475 分

问题描述

给定整数 N、M 以及长度为 N 的整数列 A=(A₁, A₂, …, A_N),其中各元素均在 1 到 M 之间。

对于该整数列 A,执行以下操作 10¹⁰⁰ 次。

从 1 到 M 的整数中,找出在 A 中出现次数最少的整数设为 v。但是,如果存在多个这样的 v,则取值最小的那个。然后,将 v 添加到 A 的末尾。

给定 Q 个查询。在第 i 个查询中,给出整数 X_i,请求在执行上述操作 10¹⁰⁰ 次之后,求 A_{X_i} 的值。

制约

1 ≤ N, M ≤ 5×10⁵
1 ≤ A_i ≤ M
1 ≤ Q ≤ 2×10⁵
1 ≤ X_i ≤ 10¹⁸

输入的所有值均为整数

输入

输入以以下格式从标准输入给出。

N M
A_1 A_2 … A_N​
Q
X_1​
X_2
⋮
X_Q

输出

请按行输出Q行。
第i行,请输出第i个查询的答案。

样例输入1

3 3
1 1 2
8
1
2
3
4
5
6
7
8

样例输入1

1
1
2
3
2
3
1
2

起初 A=(1,1,2)。在每次操作中,A 会如下变化。

  • 第一次:A 中 1、2、3 的个数分别为 2、1、0,因此取 v=3。将 v 添加到 A 的末尾,得到 A=(1,1,2,3)。
  • 第二次:A 中 1、2、3 的个数分别为 2、1、1,因此取 v=2。将 v 添加到 A 的末尾,得到 A=(1,1,2,3,2)。
  • 第三次:A 中 1、2、3 的个数分别为 2、2、1,因此取 v=3。将 v 添加到 A 的末尾,得到 A=(1,1,2,3,2,3)。

    经过10¹⁰⁰次操作后,A 变为 A=(1,1,2,3,2,3,1,2,…)。

样例输入2

7 30
20 26 3 14 4 4 9
10
31
9
21
23
97
99
30
79
57
3

样例输入2

30
2
18
21
7
9
29
19
27
3

solve

官方题解_二分树状数组

其他思路_规律推导_@lzh1020joe

Code1

太懒了于是直接复制一份@lihaochen2014

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

struct Node {
	long long v;
	int id;
};

int N, M, Q;
int A[500010];
Node X[200010];
int ans[200010];
int freq[500010];
int bit[500010];
vector<int> cnt[500010];

int lowbit(int x) {
	return x & -x;
}

void add(int x) {
	while (x <= M) {
		bit[x]++;
		x += lowbit(x);
	}
}

long long query(int x) {
	long long res = 0;
	while (x > 0) {
		res += bit[x];
		x -= lowbit(x);
	}
	return res;
}

int main()
{
	cin >> N >> M ;
	for (int i = 1; i <= N; ++i) {
		cin >> A[i] ;
		freq[A[i]]++;
	}
	cin >> Q ;
	for (int i = 1; i <= Q; ++i) {
		cin >> X[i].v ;
		X[i].id = i;
	}
	sort(X + 1, X + 1 + Q, [](Node a, Node b) {
		return a.v < b.v;
	});
	for (int i = 1; i <= M; ++i) {
		cnt[freq[i]].push_back(i);
	};
	int j = 1;
	while (j <= Q && X[j].v <= N) {
		ans[X[j].id] = A[X[j].v];
		j++;
	}
	long long marked = N;
	for (int i = 0; i <= N && j <= Q; ++i) {
		for (int num : cnt[i]) {
			add(num);
		}
		long long pmk = marked;
		marked += query(M);
		while (j <= Q && X[j].v <= marked) {
			long long k = X[j].v - pmk;
			int res = -1, l = 1, r = M;
			//cout << X[j].v << "    " << k << '\n' ;
			while (l <= r) {
				int mid = (l + r) / 2;
				//cout << mid << ' ' << query(mid) << '\n' ;
				if (query(mid) >= k) {
					res = mid;
					r = mid - 1;
				} else {
					l = mid + 1;
				}
			}
			//cout << res << '\n' ;
			ans[X[j].id] = res;
			j++;
		}
		//cout << '\n' ;
	}
	while (j <= Q) {
		ans[X[j].id] = (X[j].v - marked - 1) % M + 1;
		j++;
	}
	for (int i = 1; i <= Q; ++i) {
		cout << ans[i] << '\n' ;
	}
	return 0;
}

Code2

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e6+10;
int n,m,q;
int a[maxn],b[maxn],cnt[maxn],k;
signed main()
{
	cin >> n>> m;
	for (int i=1;i<=n;i++)
	{
		cin >> a[i];
		b[i]=(cnt[a[i]]++)*m+a[i];
		/*出现了一次,就存本身,出现了两次,就存
		m+本身,三次四次同理,可以通过输出看规律
		这样在后续处理中就可以算出位置*/ 
	}
	sort(b+1,b+n+1);
//	for (int i=1;i<=n;i++) cout << a[i] << " ";
//	cout << endl;
	for (int i=1;i<=n;i++) b[i]-=i;
	/*对上述可以通过输出观察作用*/ 
//	for (int i=1;i<=n;i++) cout << a[i] << " ";
//	cout << endl;
	cin >> q;
	while (q--)
	{
		cin >> k;
		if (k<=n) cout << a[k] << endl;
		/*对于k<=n,仍在原序列查找即可*/ 
		else
		{/*对于k>n,由于是在后方扩展,因此在原数列查不到
		   其实有两种情况:已完成一组后错(t>n)和
		   未完成一组后错(t<=n)
		   已经完成的直接%m(若%完==0,直接赋m) 
		   未完成的向后错 
		   可以合并成以下情况*/ 
			int t=lower_bound(b+1,b+n+1,k-n)-b-1;
			int ans=(k+t-n-1)%m+1;
			cout << ans << endl;
		}
	}
	return 0;
}

AC记录

F - Grid Clipping

原题指引

运行时间限制: 4 秒 / 内存限制: 1024 MiB

分数 : 500 分

问题描述

有一个 H 行 W 列的网格。我们称从上数第 r 行、从左数第 c 列的方格为方格 (r,c)。每个方格都被涂成黑色或白色,对于 k=1,2,…,N,有方格 (R_k,C_k) 被涂成黑色,其余的 HW−N 个方格被涂成白色。

请求出在这个网格中,有多少个纵向 h 行、横向 w 列的矩形区域,其包含的所有方格都是白色的。

更严格地说,请求满足以下所有条件的整数对 (r_0,c_0) 的个数:

1≤r_0≤H−h+1
1≤c_0≤W−w+1
对于满足 0≤i<h, 0≤j<w 的所有整数组合 (i,j),方格 (r_0+i,c_0+j) 被涂成白色。

制约

1≤h≤H≤10^9
1≤w≤W≤10^9
0≤N≤2×10^5
1≤R_k≤H
1≤C_k≤W
(R_k1,C_k1) ≠ (R_k2,C_k2) (k1 ≠ k2)
输入的所有值都是整数

输入

输入以以下格式从标准输入给出。

H W h w N
R_1 C_1
R_2 C_2
⋮
R_N C_N
​

输出

请输出答案。

输入样例1

3 4 2 2 3
1 3
2 4
3 1

输出样例1

2

下图中用红色圈出的两个矩形区域满足所有条件。

输入样例2

4 4 3 2 2
2 2
3 4

输出样例2

0

输入样例3

449 449 3 14 0

输出样例3

194892

输入样例4

31 9 5 7 10
14 8
8 4
18 8
12 1
8 5
9 6
18 1
14 7
5 6
26 7

输出样例4

12

solve

官方题解

翻译后

考虑满足以下所有条件的 (r_0, c_0):

1≤r_0≤H−h_1
1≤c_0≤W−w_1
存在满足 0≤i<H, 0≤j<W 的整数对 (i,j),使得格子 (r_0+i, c_0+j) 被涂成黑色

设满足这些条件的 (r_0, c_0) 的数量为 X,那么所求答案为 (H−h_1)(W−w_1)−X。以下考虑如何求出这个 X。

当存在某个被涂成黑色的格子 (R_k, C_k) 时,由它满足条件的格子为 (r_0, c_0)∈[R_k−h_1, R_k]×[C_k−w_1, C_k]。因此,只需计算这些矩形区域的并集的大小即可。

求这些矩形区域并集大小的方法有很多,这里介绍一种使用 multiset 的平面扫描方法。

每个矩形区域可以通过沿 r 轴扫描来理解:当 r=R_k−h_1 时,黑色格子出现在 [C_k−w_1, C_k],当 r=R_k+1 时消失。因此,可以按照 r 的升序,用 multiset 或 map 保持当前哪些区间有黑色格子,通过计算差分即可高速计算这些面积。

通过适当实现上述方法,可以正确解决这个问题。计算复杂度为 O(NlogN)。

显然我们不会multiset

因此我们用线段树扫描线

Code

依旧照着打@TP2010

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lson (u<<1)
#define rson (u<<1|1)
#define mid ((l+r)>>1)
const int maxn=200005;
int H,W,h,w,n;
int b[maxn<<1],num;
int tot;
int ans;
struct node
{
	int x,l,r,t;
}L[maxn<<1];
bool cmp(node nd1,node nd2){return nd1.x<nd2.x;}
int t[maxn<<4],cnt[maxn<<4];
void add(int u,int l,int r)
{
	if (cnt[u]) t[u]=b[r+1]-b[l];
	else t[u]=t[lson]+t[rson];
}
void update(int u,int ll,int rr,int op,int l=1,int r=num-1)
{
	if (ll<=l && r<=rr)
	{
		cnt[u]+=op;
		add(u,l,r);
		return;
	}
	if (ll<=mid) update(lson,ll,rr,op,l,mid);
	if (rr>mid) update(rson,ll,rr,op,mid+1,r);
	add(u,l,r);
}
signed main()
{
	cin >> H >> W >> h >> w >> n;
	for (int i=1;i<=n;i++)
	{
		int x,y;
		cin >> x >> y;
		int l=max((long long)1,x-h+1);
		int r=min(x,H-h+1)+1;
		int d=max((long long)1,y-w+1);
		int u=min(y,W-w+1)+1;
		b[++num]=l;
		b[++num]=r;
		L[++tot]={d,l,r,1};
		L[++tot]={u,l,r,-1};
	}
	sort(b+1,b+1+num);
	num=unique(b+1,b+1+num)-(b+1);
	sort(L+1,L+1+tot,cmp);
	for (int i=1;i<tot;i++)
	{
		int l=lower_bound(b+1,b+num+1,L[i].l)-b;
		int r=lower_bound(b+1,b+num+1,L[i].r)-b;
		int T=L[i].t;
		update(1,l,r-1,T);
		ans+=(L[i+1].x-L[i].x)*t[1];
	}
	cout << (H-h+1)*(W-w+1)-ans << endl;
	return 0;
}

AC记录(-_-||)

G - Many Repunit Sum 2

原题指引

运行时间限制: 2 秒 / 内存限制: 1024 MiB

分数 : 600 分

问题描述

问题文
给定正整数 N、M。

对于正整数 d,将 d 位数的 repunit 定义为整数$∑_{i=0}^{d-1} 10^i$。

求可以表示为 N 个 1 位以上 M 位以下的 repunit(不一定不同)的和的整数个数,并对 998244353 取余。

制约

1≤N≤10^5
1≤M≤10^9

输入的所有值都是整数

输入

输入以以下格式从标准输入给出。

N M

输出

请输出答案。

输入样例1

2 3

输出样例1

6

位数在 1 位以上 3 位以下的 repunit 有 1、11、111 这三种。可以表示为这三者两数之和的整数有 2、12、22、112、122、222 这六个。

输入样例2

10 10

输出样例2

92378

输入样例3

12345 123456789

输出样例3

133394021

solve

官方题解

翻译后

翻译不了一点

Code

写不了一点
侵必删

posted @ 2026-03-22 09:36  msjing  阅读(7)  评论(0)    收藏  举报