[CodeFoeces 1051E] Vasya and Big Integers(单调性 DP + Hash) | 错题本

文章目录

题目

[CodeFoeces 1051E] Vasya and Big Integers

分析

用双指针维护 d p [ i ] dp[i] dp[i] 的转移范围,问题的难点就是快速比较两个字符串的大小。比较两个字符串的大小转化为比较 LCP 的后一个字符的大小,因此 Hash + 二分即可。

要注意处理前导零的问题。
找 LCP 的时候先暴力比前面的几位,否则会被卡 Hash。
真就调了整整一天。

代码

调得很暴躁,代码不好看。

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>

typedef long long LL;
typedef unsigned long long ULL;

const int MAXN = 1000000;
const int MOD = 998244353;

int N, P, Q;
int Dp[MAXN + 5];

const ULL PRI = 2333;

ULL Pow[MAXN + 5];
struct Hash {
	int Len;
	char S[MAXN + 5];
	ULL Num[MAXN + 5];

	void Init() {
		Len = strlen(S + 1);
		for (int i = 1; i <= Len; i++)
			Num[i] = Num[i - 1] * PRI + S[i] - '0';
	}

	int Key(int lft, int rgt) const {
		return Num[rgt] - Num[lft - 1] * Pow[rgt - lft + 1];
	}
}A, L, R;

int FuckTheLead0(int st, int ed) {
	int lft = st - 1, rgt = ed;
	while (lft + 1 < rgt) {
		int mid = (lft + rgt) >> 1;
		if (A.Key(st, mid) == 0)
			lft = mid;
		else
			rgt = mid;
	}
	return lft + 1;
}

int Comp(int st, int ed, const Hash &str) {
	st = FuckTheLead0(st, ed);
	if (ed - st + 1 != str.Len)
		return ed - st + 1 > str.Len ? 1 : -1;
	int tmp = std::min(st + 100, ed);
	for (int i = st; i <= tmp; i++)
		if (A.S[i] != str.S[i - st + 1])
			return A.S[i] < str.S[i - st + 1] ? -1 : 1;
	if (tmp == ed)
		return 0;
	int lft = tmp - st, rgt = ed - st + 2;
	while (lft + 1 < rgt) {
		int mid = (lft + rgt) >> 1;
		if (A.Key(st, st + mid - 1) == str.Key(1, mid))
			lft = mid;
		else
			rgt = mid;
	}
	if (lft == ed - st + 1)
		return 0;
	return A.S[st + lft] < str.S[lft + 1] ? -1 : 1;
}

bool Lead0(int i, int len) {
	if (len == 1) return false;
	return A.S[i] == '0';
}

int main() {
	Pow[0] = 1;
	for (int i = 1; i <= MAXN; i++)
		Pow[i] = Pow[i - 1] * PRI;
	scanf("%s%s%s", A.S + 1, L.S + 1, R.S + 1);
	A.Init(), L.Init(), R.Init();
	Dp[0] = 1;
	int sum = 1, lft = 0, rgt = 0;
	std::set<int> S; S.insert(0);
//	if (A.S[1] == '1' && A.S[2] == '2' && A.S[3] == '2' && A.S[6] == '1')
//		printf("%d %d\n", Comp(1, L.Len, L), Comp(1, L.Len, R));
	Dp[L.Len] = Comp(1, L.Len, L) >= 0 && Comp(1, L.Len, R) <= 0;
	for (int i = L.Len + 1; i <= A.Len; i++) {
//		if (i == 8)
//			puts("What's the shit Fucking about?");
		int tmp = 0;
		while (rgt < i - 1 && Comp(rgt + 2, i, L) >= 0) {
			++rgt;
			bool flag = true;
			if (A.S[rgt + 1] == '0' && rgt + 1 == i)
				tmp = (tmp + Dp[rgt]) % MOD, flag = false;
			if (!Lead0(rgt + 1, i - rgt)) {
				sum = (sum + Dp[rgt]) % MOD;
				if (flag) S.insert(rgt);
			}
		}
		while ((lft <= rgt && Comp(lft + 1, i, R) > 0) || Lead0(lft + 1, i - lft)) {
			if (S.count(lft))
				sum = ((sum - Dp[lft]) % MOD + MOD) % MOD, S.erase(lft);
			lft++;
		}
		Dp[i] = sum;
//		printf("%d [%d, %d] Dp = %d sum = %d\n", i, lft, rgt, Dp[N], sum);
		sum = ((sum - tmp) % MOD + MOD) % MOD;
//		printf(" -> %d\n", sum);
	}
//	if (A.S[1] == '1' && A.S[2] == '2' && A.S[3] == '2' && A.S[6] == '1') {
//		printf("%d\n%d\n%d\n", A.Len, L.Len, R.Len);
//		for (int i = 1; i <= 20; i++)
//			printf("%c", L.S[i]);
//		puts("");
//		for (int i = 1; i <= 20; i++)
//			printf("%c", R.S[i]);
//		puts("");
//		printf("[%d, %d] Dp = %d sum = %d\n", lft, rgt, Dp[N], sum);
//	}
	printf("%d", Dp[A.Len]);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值