杂题

高精度计算

在NOIP里,一般只涉及大整数的运算

(int)(-2^{31})~(2^{31}-1)

(longspace long)(-2^{63})~(2^{63}-1)

如何表示更大范围的整数?(A=overline{a_{l-1}a_{l-2}dots a_0}=acdot10^0+a_1cdot10^1+...+a_{l-1}cdot10^{l-1})

字符串输入,倒序转换(毒瘤题(压空间的)需要压位,如每4位放进一个int。但一般一位一个int足矣)

加法&减法

模拟竖式计算过程即可,注意:

进退位为(pm1)

注意处理最高位以及进位

高精度乘法

  • 大数乘大数

    同理,(XY=sum_{i=0}^{l_x-1}sum_{j=0}^{l_y-1}x_iy_j10^{i+j})

    乘完的长度不超过 (X)(Y)的长度之和

  • 大数乘小数

    个位一次性乘完再处理进位即可

二分

注意事项:

  • 二分是对有关有序数列的操作
  • 区间的更新方式与终止条件 经常犯错 要考虑完善,

二分查找

int BinarySearch(int l,int r,int t,int x[]) {
    while(l<=r) {
        int mid=(l+r)>>1;
        if(x[mid]==t) {
            return mid;
        } else if(x[mid]<t) {
            l=mid+1;
        } else {
            r=mid-1;
        }
    }
    if(l>r) {
        return -1;
    }
}

函数复杂度(O(log_2n))

二分答案

判断条件:

  1. 问题的答案满足单调性
  2. 题目要求最大最小等

解决套路:

  1. 把可能的答案序列排序
  2. 枚举当前可能的答案区间中间值mid
  3. 验证这个答案是不是一个可行解
  4. 缩小区间,重复2
  5. 缩小到不能二分时,判断是无解还是找到了最优可行解

字符串

字符串术语

  • 子序列(不破坏相对顺序)、子串(连续的字符的集合,个数(=frac{n(n+1)}{2})),回文串

  • 前缀,后缀

  • 字符串匹配

    在文本串(T)(或集合)中,找到所有与模式串(P)匹配的个数

Trie树

解决:

  • 字符串匹配
  • 前缀匹配

基本性质:

  • 储存了字符串的前缀信息

  • 除了根节点,其他节点有且仅有一个字符

  • 从根节点开始到某一个节点连接起来就是该节点对应的前缀

  • Trie树可以在线构建

  • 插入一个新的字符串

    • 时间复杂度(O(len))
    • 空间复杂度(O(sum len))

用法:

  • 字符串查找
  • 前缀相同的字符串
  • 自动排序
  • 两两计算最长公共前缀
  • 扩展:建二进制Trie树,存二进制数

存储实现:

struct Node {
    int cnt;
    Node *ch[30]= {0};
    //或者
    int nxt[30]= {0};//转指针为数组
};

字符串Hash

(该算法如果想被卡比较容易,所以不推荐

字符串( ightarrow)数字:

hash=0;
p=19260817LL,q=1e9+7LL;
for(int i=0; i<len; i++) {
    hash=(hash*p%q+s[i]-'a'+1)%q;
}

KMP算法

  • 加速单模板串,多个文本串或长文本串的匹配

失配数组(nxt数组)

  • 失配数组(nxt[i]=j)表示当第(i)位失配时可以快速移动到(j)

  • (nxt)数组代可以通过自己配对自己得到

  •   void getnext(char *s,int len,int *nxt) {
          int x=0;
          nxt[0]=-1;
          for(int i=1; i<len; i++) {
              while(x!=-1&&s[x]^s[x-1]) {
                  x=nxt[x];
              }
              nxt[i+1]=++x;
          }
      }
      //kmp函数同理
    

马拉车

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
char s[maxn * 2], str[maxn * 2];
int Len[maxn * 2], len;
void getstr() {//重定义字符串
	int k = 0;
	str[k++] = '@';//开头加个特殊字符防止越界
	for (int i = 0; i < len; i++) {
		str[k++] = '#';
		str[k++] = s[i];
	}
	str[k++] = '#';
	len = k;
	str[k] = 0;//字符串尾设置为0,防止越界
}
int manacher() {
	int mx = 0, id;//mx为最右边,id为中心点
	int maxx = 0;
	for (int i = 1; i < len; i++) {
		if (mx > i) Len[i] = min(mx - i, Len[2 * id - i]);//判断当前点超没超过mx
		else Len[i] = 1;//超过了就让他等于1,之后再进行查找
		while (str[i + Len[i]] == str[i - Len[i]]) Len[i]++;//判断当前点是不是最长回文子串,不断的向右扩展
		if (Len[i] + i > mx) {//更新mx
			mx = Len[i] + i;
			id = i;//更新中间点
			maxx = max(maxx, Len[i]);//最长回文字串长度
		}
	}
	return (maxx - 1);
}
int main() {
	scanf("%s", s);
	len = strlen(s);
	getstr();
	printf("%d
",manacher());
	return 0;
}

题目

例题

  1. CF1181B

    从中间开始向两边找,找到第一个划分即为所求

  2. CF900B

    模拟(e.g (frac{24}{7}:(24,0) ightarrow(3,3) ightarrow(30,3)...)

  3. AT2827

    贪心+二分查找优化时间复杂度

  4. P2678

    二分选手的最小跳跃距离,如果距离小于选手的最小跳跃距离则把这块石头移走,移走数目大于(m)时不满足题意,反之亦然。

推荐练习

P2954,P1631,HDU1358 Period,HDU2203,SP7586

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。

限于本人水平,如果文章有表述不当之处,还请不吝赐教。

原文地址:https://www.cnblogs.com/Sam2007/p/12409142.html