zoukankan      html  css  js  c++  java
  • 7.16复习笔记

    今天就不算是复习笔记了吧,因为也没有具体的讲解,不过对于这些我确实没什么想说的

    因为下午调题调的比较嗨,所以就把一些比较简单的都放到了今天统一写了一下

    一、AC自动机

    一些经典的问题:
    1. 求各个模式串在文本串中出现了几次:

      方法: 在AC自动机上先跑一边文本串,记录一下每个点被经过的次数,那么单词被经过的次数就是他的结尾在fail树中的子树的权值和

    2. 找是否有一个匹配不上匹配串的环:

      方法: 把所有能匹配上匹配串的节点匹配记录下来,dfs从跟开始走所有我可以走的点,如果我走到了一个之前走过的点就说明出环了

      实现: 一边调用fail一边更新当前点是否能走,因为fail记录的是前缀等于后缀,后缀深度一定比前缀大,所以fail一定在之前就走过了

    3. (n)个串总共出现(m)次的最短长度

      方法: 求两两单词结尾之间的最短距离,初始矩阵(a_{i,j})表示结尾(i)和结尾(j)之间不经过其他结尾的最短距离,矩阵快速幂(m-1)

      更新成(a_{i,j})表示结尾(i)到结尾(j)之间经过其他(m-2)个结尾的最小距离,现在总共经过了(m)个单词结尾,再加上第一个单词的长度,就是最短长度,一共(nm)种情况取最小的一种

    4. 每次删掉文本串中的最早出现的模式串,剩下的字符继续拼在一起

      方法: 跑AC自动机,模拟栈,发现匹配到一个单词,把属于这个单词的字符全部弹出,继续从之前匹配的位置继续往下找

    int tot = 1;
    void getTrie(char s){
    	int root = 1,len = strlen(s+1);
    	for (int i = 1;i <= len;i++){
    		int nxt = s[i]-'a';
    		if (!ch[root][i]) ch[root][i] = ++tot;
    		root = ch[root][i];
    	}
    }
    void getFail(){
    	queue<int> q;q.push(1);
    	for (int i = 0;i < 26;i+++) ch[0][i] = 1;
    	while (!q.empty()){
    		int root = q.front();q.pop();
    		for (int i = 0;i < 26;i++){
    			if (!ch[root][i]) ch[root][i] = ch[fail[root]][i];
    			else{
    				fail[ch[root][i]] = ch[fail[root]][i];
    				q.push(ch[root][i]);
    			}
    		}
    	}
    }
    

    二、tarjan 2-SAT

    注意我连边的时候一定是xxx时一定yyy的时候,让xxx向yyy连边

    tajan染色后方案为编号小的那一个

    void tarjan(int x){
    	dfn[x] = low[x] = ++cnt;
    	st[++top] = x;
    	for (int i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (!dfn[to]){
    			tarjan(to);
    			low[x] = min(low[x],low[to]);
    		}
    		else if (!col[to]) low[x] = min(low[x],dfn[to]);
    	}
    	if (dfn[x] == low[x]){
    		int y;++color;
    		while (y = st[top--]){
    			col[y] = color;
    			if (x == y) break;
    		}
    	}
    }
    

    割点 :在一个无向图中,如果删除某个顶点,这个图就不再连通(任意两点之间无法相互到达),那么这个顶点就是这个图的割点。

    割边(桥):在一个无向图中删除某条边后,图不再连通,那么这条边就是这个图的割边(也叫作桥)

    求法

    • 割点: 一个顶点(x)是割点,当且仅当满足:
    1. (x)为树根,且(x)有多于一个子树。

    2. (x)不为树根,且满足(x)(to)在搜索树中的父亲,并使得(low_{to}le dfn_x).(因为删去(x)(to)以及(to)的子树不能到达(x)的其他子树以及祖先)

    code:

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a; 
    }
    const int maxn = 1e5+10;
    int n,m;
    struct node{
    	int to,next;
    }ed[maxn*2];
    int head[maxn*2],tot;
    void add(int u,int to){
    	ed[++tot].to = to;
    	ed[tot].next = head[u];
    	head[u] = tot;
    }
    int dfn[maxn],low[maxn],flag[maxn],cnt,child;
    void tarjan(int x,int fa){
    	dfn[x] = low[x] = ++cnt;
    	for (int i = head[x];i;i = ed[i].next){
    		int to = ed[i].to;
    		if (!dfn[to]){
    			tarjan(to,fa);
    			low[x] = min(low[x],low[to]);
    			if (low[to] >= dfn[x]&&x != fa) flag[x] = 1;	
    			if (x == fa) child++;
    		}
    		low[x] = min(low[x],dfn[to]);
    	}
    	if (child >= 2&&x == fa) flag[x] = 1;
    }
    int main(){
    	n = read(),m = read();
    	for (int i = 1;i <= m;i++){
    		int x = read(),y = read();
    		add(x,y),add(y,x);
    	}
    	for (int i = 1;i <= n;i++){
    		if (!dfn[i]) child = 0,tarjan(i,i);
    	}
    	int tot = 0;
    	for (int i = 1;i <= n;i++){
    		if (flag[i]) tot++;
    	}
    	printf("%d
    ",tot);
    	for (int i = 1;i <= n;i++){
    		if (flag[i]) printf("%d ",i);
    	}
    	return 0;
    } 
    
    • 割边:
    1. 一条无向边((x,to))是桥,满足(low_{to}>dfn_x).(因为(to)想要到达(x)的父亲必须经过((x,to))这条边,所以删去这条边图不连通)

    2. 实现时因为是无向图,建反边两条边都要标记上,边从(1)开始编号,正向边(x)的反向边就是(x)^(1)

    code:

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 3e6+10;
    int n,m;
    struct node{
    	int to,next;
    }ed[maxn*2];
    int head[maxn*2],tot = 1;
    void add(int u,int to){
    	ed[++tot].to = to;
    	ed[tot].next = head[u];
    	head[u] = tot;
    }
    int dfn[maxn],low[maxn],cnt,res;
    int bridge[maxn];
    void tarjan(int x, int fa) {
        dfn[x] = low[x] = ++cnt;
        for (int i = head[x];i;i = ed[i].next) {
            int to = ed[i].to;
            if (!dfn[to]) {
                tarjan(to, i);
                low[x] = min(low[x],low[to]);
                if (low[to] > dfn[x])
                    bridge[i] = bridge[i^1] = true;
            }
            else if (i != (fa^1)) low[x] = min(low[x],dfn[to]);
        }
    }
    int main(){
    	n = read(),m = read();
    	for (int i = 1;i <= m;i++){
    		int u = read(),v = read();
    		add(u,v),add(v,u);
    	}
    	for (int i = 1;i <= n;i++){
    		if (!dfn[i]) tarjan(i,0);
    	}
    	int res = 0;
        for (int i = 2; i < tot; i+=2)
            if (bridge[i]) res++;
    	printf("%d
    ",res);
    	return 0;
    }
    

    三、manacher

    因为今天没有把NOI2020做完,所以把之后几天的复习计划提前了,毕竟计划做完了才能心安理得

    解决回文串问题,其实不难发现在解决字符串问题的时候,我们通常都想要充分的利用之前处理过的信息

    void manacher(){
    	for (int i = 1,r = 0,mid = 0;i <= cnt;i++){
    		if (i <= r) f[i] = min(f[(mid << 1)-i],r-i+1);
    		while (a[i-f[i]] == a[i+f[i]]) f[i]++;
    		if (f[i]+i > r) r = f[i]+i-1,mid = i;
    	}
    }
    

    四、Nim博弈SG函数

    我觉得我之前写的博客就已经很详细啦,因为不涉及什么算法,就是打表找规律,这里就不总结了

    五、欧拉函数

    求不超过n且与n互质的正整数的个数

    void Euler(){
    	for (int i = 2;i < n;i++){
    		if (!E[i]){
    			for (int j = i;j < n;j += i){
    				if (!E[j]) E[j] = j;
    				E[j] = E[j]/i*(i-1); 
    			}
    		}
    	}
    }
    

    六、线性基

    往线性基里插入一个数

    void add(int x){
    	for (int i = 30;i >= 0;i--){
    		if (x&(1 << i)){
    			if (d[i]) x ^= d[i];
    			else{
    				d[i] =  x;
    				break;
    			}
    		}
    	}
    }
    

    查询最大异或和

    int getmax(){
    	int res = 0;
    	for (int i = 30;i >= 0;i--){
    		if (res^d[i] > d[i]) res ^= d[i];
    	}
    	return res;
    }
    

    查询k大异或和

    void init(){
    	for (int i = 1;i <= 30;i++){
    		for (int j = 1;j <= i;j++){
    			if (d[i]&(1 << (j-1))) d[i] ^= d[j-1];
    		}
    	}
    }
    int kth(int k){
    	if (k == 1&&tot < n) return 0;
    	if (tot < n) k--;
    	if (pow(2,tot) <= k) retun -1;
    	init();
    	int ans = 0;
    	for (int i = 0;i <= 30;i++){
    		if (d[i]){
    			if (k&1) ans ^= d[i];
    			k >>= 1;
    		}
    	}
    	return ans;
    }
    
  • 相关阅读:
    算法训练 P1103
    算法训练 表达式计算
    算法训练 表达式计算
    基础练习 时间转换
    基础练习 字符串对比
    Codeforces 527D Clique Problem
    Codeforces 527C Glass Carving
    Codeforces 527B Error Correct System
    Codeforces 527A Glass Carving
    Topcoder SRM 655 DIV1 250 CountryGroupHard
  • 原文地址:https://www.cnblogs.com/little-uu/p/15020002.html
Copyright © 2011-2022 走看看