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

  • 相关阅读:
    cocos2d 多点触摸
    mac 下安装node.js
    黑鹰破解笔记(2)
    OD使用心得笔记二
    淘宝店开始进行审核
    黑鹰破解笔记(1)
    Lisp笔记1
    OD使用心得笔记一
    怒马
    近段时间的web开发
  • 原文地址:https://www.cnblogs.com/xixike/p/15291432.html
Copyright © 2011-2022 走看看