zoukankan      html  css  js  c++  java
  • 洛谷 CF898F Restoring the Expression

    Description

    CF898F Restoring the Expression

    Solution

    很明显的一道哈希题,那么我们考虑哈希能否支持加法。

    我们通过列举大量式子发现,普通的哈希是不行的,但是我们换一个乘数,也就是令 (Base = 10),那么是不是就可以支持加法了呢。

    但是这样一来,冲突的概率就大了很多,所以要双哈希,而且由于是 CF 的题这道题非常毒瘤的把 (998244353)(1000000007) 等常见模数全部卡掉了 (QwQ),恶心至极。所以要换一些模数。

    然后就是具体过程。

    我们可以枚举等号的位置,假设等号后面的数长为 (len),由于加法最多进一位,所以加号前的数长度为 (len)(len - 1)加号后面的数同理。

    另外注意这道题还要判前导 0。

    这道题最麻烦的就是边界的处理,我整整做了一个晚上啊啊啊啊啊啊啊。

    Code

    (有注释)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define ll long long
    
    using namespace std;
    
    const ll N = 1e6 + 10;
    const ll mod1 = 1e9 + 21;//双哈希模数
    const ll mod2 = 1e9 + 33;
    ll n, pos1 = N, pos2 = N;//pos1: 加号位置	pos2: 等号位置
    ll fx[N], f[N];//双哈希两个数组
    ll gx[N], g[N];
    char s[N];
    
    inline ll Hash1(ll l, ll r){//去第一个哈希值
    	return (f[r] - f[l - 1] * fx[r - l + 1] % mod1 + mod1) % mod1;
    }
    
    inline ll Hash2(ll l, ll r){//取第二个哈希值
    	return (g[r] - g[l - 1] * gx[r - l + 1] % mod2 + mod2) % mod2;
    }
    
    inline bool check(int l, int r){//判断是否有前导0,1为没有,0为有
    	if(s[l] != '0') return 1;//第一位不为0,直接返回1
    	for(int i = l + 1; i <= r; i++)
    		if(s[i] == '0') return 0;
    	return 1;
    }
    
    inline bool checkr1(int len, int n, int flag){//判断第二个加数长度为 len 或 len - 1 时,等式是否成立。flag = 0时,长度为 len - 1,flag = 1时,长度为 len
    	int l = n - (len << 1) + flag;//l 为第一个加数右边界
    	if(l < 1) return 0;
        //第一个加数 + 第二个加数 == 和
    	return ((Hash1(1, l) + Hash1(l + 1, n - len)) % mod1) == Hash1(n - len + 1, n) && check(l + 1, n - len);
    }
    
    inline bool checkr2(int len, int n, int flag){//第二个哈希
    	int l = n - (len << 1) + flag;
    	if(l < 1) return 0;
    	return ((Hash2(1, l) + Hash2(l + 1, n - len)) % mod2)== Hash2(n - len + 1, n) && check(l + 1, n - len);
    }
    
    inline bool checkl1(int len, int n, int flag){//判断第一个加数的情况,其它同上
    	int l = len + flag - 1;
    	if(l < 1) return 0;
    	return ((Hash1(1, l) + Hash1(l + 1, n - len)) % mod1) == Hash1(n - len + 1, n) && check(l + 1, n - len);
    }
    
    inline bool checkl2(int len, int n, int flag){
    	int l = len + flag - 1;
    	if(l < 1) return 0;
    	return ((Hash2(1, l) + Hash2(l + 1, n - len)) % mod2) == Hash2(n - len + 1, n) && check(l + 1, n - len);
    }
    
    inline void updater(int len, int flag){//当第二个加数符合条件时,更新加号和等号的位置
    	if(n - (len << 1) + flag < pos1) pos1 = n - (len << 1) + flag, pos2 = n - len;
    }
    
    inline void updatel(int len, int flag){//同理
    	if(len + flag - 1 < pos1) pos1 = len + flag - 1, pos2 = n - len;
    }
    
    signed main(){
    	//  freopen("a.in", "r", stdin);
    	//  freopen("a.out", "w", stdout);
    	scanf("%s", s + 1);
    	n = strlen(s + 1);
    	fx[0] = 1, gx[0] = 1;
    	for(ll i = 1; i <= n; i++){
    		fx[i] = fx[i - 1] * 10 % mod1;
    		f[i] = (f[i - 1] * 10 + s[i] - '0') % mod1;
    		gx[i] = gx[i - 1] * 10 % mod2;
    		g[i] = (g[i - 1] * 10 + s[i] - '0') % mod2;
    	}
    	for(int len = n / 3; len <= (n >> 1); len++){
    		if(!check(n - len + 1, n)) continue;//和 有前导0,直接跳过
    		if(checkr1(len, n, 0) && checkr2(len, n, 0)) updater(len, 0);//第二个加数长度为 len - 1
    		if(checkr1(len, n, 1) && checkr2(len, n, 1)) updater(len, 1);//第二个加数长度为 len
    		if(checkl1(len, n, 0) && checkl2(len, n, 0)) updatel(len, 0);//第一个加数长度为 len - 1
    		if(checkl1(len, n, 1) && checkl2(len, n, 1)) updatel(len, 1);//第二个加数长度为 len
    	}
    	for(ll i = 1; i <= pos1; i++)//输出
    		putchar(s[i]);
    	putchar('+');
    	for(ll i = pos1 + 1; i <= pos2; i++)
    		putchar(s[i]);
    	putchar('=');
    	for(ll i = pos2 + 1; i <= n; i++)
    		putchar(s[i]);
    	puts("");
    	return 0;
    }
    

    本文来自博客园,作者:xixike,转载请注明原文链接:https://www.cnblogs.com/xixike/p/15291432.html

  • 相关阅读:
    Roslyn 语法树中的各种语法节点及每个节点的含义
    WPF 使用 WindowChrome,在自定义窗口标题栏的同时最大程度保留原生窗口样式(类似 UWP/Chrome)
    在制作跨平台的 NuGet 工具包时,如何将工具(exe/dll)的所有依赖一并放入包中
    如何在 .NET 库的代码中判断当前程序运行在 Debug 下还是 Release 下
    像黑客一样!Chrome 完全键盘操作指南(原生快捷键 + Vimium 插件)
    .NET 使用 XPath 来读写 XML 文件
    XML 的 XPath 语法
    WPF 中使用附加属性,将任意 UI 元素或控件裁剪成圆形(椭圆)
    Windows 10 四月更新,文件夹名称也能区分大小写?
    C#/.NET 中推荐的 Dispose 模式的实现
  • 原文地址:https://www.cnblogs.com/xixike/p/15291432.html
Copyright © 2011-2022 走看看