ABC339 A-G

Japan Registry Services (JPRS) Programming Contest 2024 (AtCoder Beginner Contest 339) - AtCoder

离AK ABC最近的一集,写完F还剩十分钟聊天去了,赛后一看题一眼主席树贴板子改一改十分钟过了...前几题感觉真的阅读理解...

A - TLD

题意:

给出一个由小写英文字符和'.'构成的字符串,输出该字符串中最后一个'.'之后的部分

代码:

char ch[N];
void solve()
{
	scanf("%s", ch + 1);
	int p = 1;
	for (int i = 1; ch[i]; ++i)
	{
		if (ch[i] == '.')
			p = i + 1;
	}
	printf("%s", ch + p);
}

B - Langton's Takahashi

题意:

有一张H*W的矩阵,刚开始矩阵的每个格子都是白色的,这个矩阵上下左右是成环的(也就是说每一行的第w个格子右边是这行的第一个格子,列上同理)

你刚开始位于(1, 1)且面朝上,你将进行以下操作N次:

1、若当前格子是白色的,将这个格子染成黑色,右转(顺时针)90°,向前走一格

2、若当前格子是黑色的,将这个格子染成白色,左转(逆时针)90°,向前走一格

输出经过所有操作后的矩阵

题解:

若至阅读理解题,模拟即可

int dx[] = { -1,0,1,0 }, dy[] = { 0,1,0,-1 }, mp[N][N];
void solve()
{
	int h, w, n, x = 1, y = 1, op = 0;
	scanf("%d%d%d", &h, &w, &n);
	while (n--)
	{
		if (!mp[x][y])
			mp[x][y] = 1, op = (op + 1) % 4;
		else
			mp[x][y] = 0, op = (op + 3) % 4;
		x += dx[op], y += dy[op];
		x = (x - 1 + h) % h + 1, y = (y - 1 + w) % w + 1;
	}
	for (int i = 1; i <= h; ++i)
	{
		for (int j = 1; j <= w; ++j)
		{
			if (mp[i][j])printf("#");
			else printf(".");
		}
		printf("\n");
	}
}

C - Perfect Bus

题意:

有辆公交车,你不知道初始时它装了多少乘客人,接下来这两公交车经过了N站,每一站乘客的人数变化为Ai(为正则为上车人数,为负则为下车人数),问经过这N站之后车上乘客数量的最小值

题解:

设初始人数为0,模拟一下,设模拟过程中的最小值为mn,则-mn为最小的初始乘客数

void solve()
{
	LL n, s = 0, mn = 0, mx = 0;
	scanf("%lld", &n);
	for (int i = 1, x; i <= n; ++i)
	{
		scanf("%d", &x);
		s += x;
		mn = min(mn, s);
	}
	printf("%lld\n", -mn + s);
}

D - Synchronized Players

题意:

给出一张N*N的地图,'.'为能走的道路,'#'为不能走的墙壁,'P'为两个玩家,你可以对两名玩家进行以下操作任意次:让两名玩家同时尝试向上/下/左/右移动一格,若能移动则移动,不能在站在原地。问最少多少步使两名玩家位置重合,不能输出-1

题解:

考虑到N很小,设dp[x1][y1][x2][y2]为使得玩家1在(x1, y1)位置,玩家2在(x2, y2)位置的最小操作数,bfs转移一下即可,数据极限情况挺极限的,用map存T了一发...

const LL N = 6e1 + 10;
char mp[N][N];
int dp[N][N][N][N];
queue<pair<PII, PII>>q;
int dx[] = { 0,0,1,-1 }, dy[] = { 1,-1,0,0 };
PII op(PII p, int t)
{
	int tx = p.first + dx[t], ty = p.second + dy[t];
	if (mp[tx][ty] != '#')return { tx,ty };
	return p;
}
void solve()
{
	int n;
	scanf("%d", &n);
	vector<PII>s;
	for (int i = 0; i <= n + 1; ++i)
	{
		for (int j = 0; j <= n + 1; ++j)
			mp[i][j] = '#';
	}
	for (int i = 1; i <= n; ++i)
	{
		getchar();
		for (int j = 1; j <= n; ++j)
		{
			mp[i][j] = getchar();
			if (mp[i][j] == 'P')
				s.push_back({ i,j });
		}
	}
	q.push({ s[0],s[1] });
	dp[s[0].first][s[0].second][s[1].first][s[1].second] = 1;
	while (q.size())
	{
		PII a = q.front().first, b = q.front().second;
		if (a == b)
		{
			printf("%d\n", dp[a.first][a.second][b.first][b.second] - 1);
			return;
		}
		q.pop();
		for (int i = 0; i < 4; ++i)
		{
			PII ta = op(a, i), tb = op(b, i);
			if (!dp[ta.first][ta.second][tb.first][tb.second])
			{
				dp[ta.first][ta.second][tb.first][tb.second] = dp[a.first][a.second][b.first][b.second ]+ 1;
				q.push({ ta,tb });
			}
		}
	}
	printf("-1\n");
}

E - Smooth Subsequence

题意:

给出一个长度为N的数组A,求一个最长的子序列使得相邻元素之间差值的绝对值不大于D

题解:

线段树优化DP,设dp[x]为以值x结尾时最长的子序列长度,转移为dp[x]=max({dp[i], x - d <= i <= x + d}) + 1,处理一下两边界然后线段树优化即可

const LL N = 5e5 + 10;
#define ls(i) (i<<1)
#define rs(i) (i<<1|1)
int a[N], tr[N << 2];
void updata(int pos, int data, int l, int r, int i)
{
	if (l == r)
	{
		tr[i] = data;
		return;
	}
	int mid = l + r >> 1;
	if (pos <= mid)updata(pos, data, l, mid, ls(i));
	else updata(pos, data, mid + 1, r, rs(i));
	tr[i] = max(tr[ls(i)], tr[rs(i)]);
}
int query(int ql, int qr, int l, int r, int i)
{
	if (ql <= l && r <= qr)
		return tr[i];
	int mid = l + r >> 1, res = 0;
	if (mid >= ql)res = max(res, query(ql, qr, l, mid, ls(i)));
	if (mid < qr)res = max(res, query(ql, qr, mid + 1, r, rs(i)));
	return res;
}
void solve()
{
	int n, d;
	scanf("%d%d", &n, &d);
	for (int i = 1, x; i <= n; ++i)
	{
		scanf("%d", &x);
		int l = max(1, x - d), r = min((int)N, x + d);
		updata(x, query(l, r, 1, N, 1) + 1, 1, N, 1);
	}
	printf("%d\n", query(1, N, 1, N, 1));
}

F - Product Equality

题意:

给出一个长度为N的大数数组,求符合以下条件的三元组(i , j, k)数量:

1 <= i, j, k <= n,Ai * Aj = Ak

题解:

随机化哈希,刚好前段时间做过一道随机化哈希的题...

显然用高精度是做不了的,然后这题要求啊Ai * Aj = Ak显然在取模意义下也是成立的,那就可以对每个数都取模,这样问题就变成了1e3个普通32(或64)位整数之间的问题,枚举i, j然后查询符合条件的Ak数量(比如说存进map)即可,取模数建议生成随机数取,还可以多模,这样多能降低被卡的概率

const LL N = 1e3 + 10;
string s[N];
array<int, 6>a[N];
map<array<int, 6>, int>mp;
int mod[10] = { 998244353,51646229,68861641,91815541,91815541,(int)1e9 + 7 };
//atc没有hack这里就随便取几个数了(随机生成高质量随机数还不会用...)
//有点抽象的六模的哈希
array<int, 6> make_ar(string& t)
{
	array<int, 6>res = { 0,0,0,0,0,0 };
	for (auto& c : t)
	{
		int f = c - '0';
		for (int i = 0; i < 6; ++i)
			res[i] = ((LL)res[i] * 10 + f) % mod[i];
	}
	return res;
}
array<int, 6> operator*(array<int, 6>x, array<int, 6>y)
{
	array<int, 6>res;
	for (int i = 0; i < 6; ++i)
		res[i] = (LL)x[i] * y[i] % mod[i];
	return res;
}
void solve()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
	{
		cin >> s[i];
		a[i] = make_ar(s[i]);
		++mp[a[i]];
	}
	int ans = 0;
	for (int i = 1; i <= n; ++i)
	{
		for (int j = 1; j <= n; ++j)
		{
			array<int, 6>res = a[i] * a[j];
			if (mp.find(res) != mp.end())
				ans += mp[res];
		}
	}
	printf("%d\n", ans);
}

G - Smaller Sum

题意:

给出一个长度为N的数组A,有Q个询问,每个询问要求求出从子串Li, Ri中所有不小于Xi的元素之和(强制在线)

题解:

一眼看着像主席树,然后对主席树改一改,维护的东西多加个维护sum就行了

const LL N = 2e5 + 10, M = 1e9 + 10;
#define ls(x) (tr[x].ls)
#define rs(x) (tr[x].rs)
#define s(x) tr[x].s
#define sum(x) tr[x].sum
struct node
{
	int ls, rs, s;
	LL sum;
}tr[32 * N];//logM * N
int root[N], tot;
void push_up(int i)
{
	s(i) = s(ls(i)) + s(rs(i));
	sum(i) = sum(ls(i)) + sum(rs(i));
}
void updata(int pos, int data, int l, int r, int last, int now)
{
	if (l == r)
	{
		s(now) = s(last) + data;
		sum(now) = sum(last) + (LL)data * pos;
		return;
	}
	ls(now) = ls(last), rs(now) = rs(last);
	int mid = l + r - 1 >> 1;
	if (pos <= mid)
		updata(pos, data, l, mid, ls(last), ls(now) = ++tot);
	else
		updata(pos, data, mid + 1, r, rs(last), rs(now) = ++tot);
	push_up(now);
}
void updata(int pos, int data, int last, int now)
{
	updata(pos, data, -M, M, last, now);
}
LL query_range_sum(int ql, int qr, int l, int r, int last, int now)
{
	if (ql <= l && r <= qr)return sum(now) - sum(last);
	LL mid = l + r - 1 >> 1, res = 0;
	if (ql <= mid)res += query_range_sum(ql, qr, l, mid, ls(last), ls(now));
	if (qr > mid)res += query_range_sum(ql, qr, mid + 1, r, rs(last), rs(now));
	return res;
}
LL query_range_sum(int ql, int qr, int last, int now)
{
	return query_range_sum(ql, qr, -M, M, last, now);
}
void solve()
{
	int n;
	scanf("%d", &n);
	for (int i = 1, x; i <= n; ++i)
	{
		scanf("%d", &x);
		root[i] = ++tot;
		updata(x, 1, root[i - 1], root[i]);
	}
	int q;
	scanf("%d", &q);
	LL ans = 0;
	while (q--)
	{
		LL l, r, x;
		scanf("%lld%lld%lld", &l, &r, &x);
		l ^= ans, r ^= ans, x ^= ans;
		ans = query_range_sum(1, x, root[l - 1], root[r]);
		printf("%lld\n", ans);
	}
}

看完题意感觉像是主席树,然后一想不对好像可以离线,再一看离线被ban了,再想想主席树做法发现完全能做...板子改改一发过了

  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值