• “蔚来杯“2022牛客暑期多校训练营9 补题题解(A)


    “蔚来杯“2022牛客暑期多校训练营9


    A Car Show

    双指针算法,先移动右指针到满足从1~ i的区间内1~m的数每个至少有一个,之后移动左指针,只要满足区间内1 ~ m的数每个至少还有一个,左指针j就能一直移动,直到不满足条件。
    这题还有个要注意的点是需要define intg long long,但是题中给的n、m、a都是小于1e6的,唯一可能的就是ans会爆int。

    #include
    
    using namespace std;
    
    #define int long long
    
    const int N = 1e5 + 10;
    
    int n, m;
    int a[N];
    int ans;  //记录答案
    int res;  //记录不同数的个数
    int cnt[N];  //记录每个数出现过没 
    
    signed main()
    {
    	cin>>n>>m;
    	
    	for(int i = 1; i <= n; i ++ ){
    		cin>>a[i];
    	}
    	
    	int j = 1;  //左指针 
    	for(int i = 1; i <= n; i ++ ){  //移动区间右指针 
    		if(!cnt[a[i]]){
    			res ++ ;
    		}
    		cnt[a[i]] ++ ;
    		if(res == m){
    			while(j < i && cnt[a[j]] > 1){
    				ans += n - i + 1;
    				cnt[a[j]] -- ;
    				j ++ ;  //移动左指针 
    			}
    			ans += n - i + 1;  //这个加上的是最后一个符合条件的区间,此时a[j]=1,在while不会被计算 
    			cnt[a[j]] -- ;
    			j ++ ;
    			res -- ;
    		}
    	}
    	cout<<ans<<endl;
    	return 0; 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    B Two Frogs

    请添加图片描述

    #include
    
    using namespace std;
    
    #define int long long
    
    const int N = 8010, mod = 998244353;
    
    int dp[N][N];  //dp[i][j]表示跳了i次到达j点的概率
    int a[N];
    int n;
    int infac[N];  //逆元数组 
    int ans;
    int di[N];  //一维差分数组
    
    int qmi(int a, int b){
    	int res = 1;
    	while(b){
    		if(b & 1) res = res * a % mod;
    		a = a * a % mod;
    		b >>= 1; 
    	}
    	return res;
    }
    
    void init(){  //求逆元 
    	for(int i = 1; i < 8000; i ++ ){
    		infac[i] = qmi(i, mod - 2); 
    	}
    }
    
    signed main()
    {
    	init();
    	cin>>n;
    	for(int i = 1; i < n; i ++ ) cin>>a[i];
    	
    	dp[0][1] = 1;
    	
    	for(int i = 1; i < n; i ++ ){  //枚举起点 
    		
    		for(int j = 1; j < n; j ++ ){   
    			di[j + 1] = (di[j + 1] + dp[i - 1][j] * infac[a[j]] % mod + mod) % mod;
    			di[j + a[j] + 1] = (di[j + a[j] + 1] - dp[i - 1][j] * infac[a[j]] % mod + mod) % mod;
    		}
    		
    		for(int j = 1; j <= n; j ++ ){  //这里是在对到达i点的所有步骤进行差分求前缀和  
    			dp[i][j] = (dp[i][j] + dp[i][j - 1] + di[j]) % mod;
    		}
    		
    		memset(di, 0, sizeof di);
    	}
    	
    	for(int i = 0; i < n; i ++ ){  //同时到达0点也包括在内 
    		ans = (ans + dp[i][n] * dp[i][n] % mod) % mod;
    	}
    	
    	cout<<ans<<endl;
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    G Magic Spells

    一道题搞了两天,终于搞懂了回文自动机,还学会了马拉车算法,也算收货不小,这题是回文自动机套板子
    回文自动机讲解看这里

    #include
    
    using namespace std;
    
    const int N = 3e5 + 10;
    
    int tr[N][27];  //建树
    int n;
    int ans;  //记录答案
    bool cnt[5][N];  //cnt[i][j]表示在第i个魔法串内j串是否存在
    int fail[N];  //fail[i]指向i的最长回文后缀子串的节点(个人认为是回文树的灵魂数组)
    int len[N];  //记录节点长度
    int idx;  //记录节点个数
    int last;  //记录上一个新加入的节点
    char s[N];  //存储字符串
    int now;
    
    int newnode(int x){  //创建新节点 
    	len[idx] = x;
    	return idx ++ ;  //返回之后idx再++ 
    } 
    
    int getfail(int x, int n){  跳fail指针直到找到后缀回文为止,用到的两个参数都是字符串的下标 
    	while(s[n - len[x] - 1] != s[n]) x = fail[x];
    	return x;
    }
    
    void init(){
    	newnode(0), newnode(-1);
    	fail[0] = 1;	
    }
    
    void insert(int j, int i){
    	int t = s[j] - 'a';
    	//找到可以回文的位置
    	int p = getfail(last, j);  //返回的是节点编号
    	if(!tr[p][t]){
    		//如果有了转移就不用建了,否则要新建 
    	    //前后都加上新字符,所以新回文串长度要加2
    		now = newnode(len[p] + 2);
    		//因为fail指向的得是原串的严格后缀,所以要从p的fail开始找起
    		fail[now] = tr[getfail(fail[p], j)][t];
    		//记录转移
    		tr[p][t] = now;
    	}
    	//以下两个变量不能直接用now赋值,因为tr[p][t]可能存在,此时now的值就会赋错 
    	last = tr[p][t];
    	cnt[i][last] = true;
    }
    
    int main()
    {
    	ios::sync_with_stdio(0);
    	cin.tie(0);
    	
    	cin>>n;
    	
    	init();  //初始化一定不能忘了
    	 
    	//先把所有串放到一个树内
    	for(int i = 0; i < n; i ++ ){
    		last = 0;
    		cin>>s + 1;
    		s[0] = -1;
    		for(int j = 1; s[j]; j ++ ) insert(j, i);
    	}
    	
    	for(int i = idx - 1; i > 1; i -- ){  //遍历除两个根节点之外的每个节点,沿着树从下往上遍历,因为树上层的状态没有被更新 
    		for(int j = 0; j < n; j ++ ){  //遍历n个串
    			cnt[j][fail[i]] |= cnt[j][i];
    		}
    		
    		bool ok = true;
    		
    		for(int j = 0; j < n; j ++ ){
    			if(!cnt[j][i]) ok = false;
    		}
    		ans += ok;
    	}
    	
    	cout<<ans<<endl;  //答案是所有串中都包含的回文子串的个数 
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    E Longest Increasing Subsequence

    构造类型题,先想最基础的构造,然后根据题意逐步优化
    雨巨讲题 从39min开始看
    代码是借鉴这个大佬的:这个讲解也很强,和雨巨的结合着更容易理解 传送门

    #include
    
    using namespace std;
    
    const int N = 1e6 + 10;
    
    int T;
    int n;
    int len;  //长度
    int cnt;  //排列的长度
    int cur;  //赋值游标
    int ans[N];  //答案数组
    
    int main()
    {
    	cin>>T;
    	while(T -- ){
    		cnt = 0;
    		len = 0;	
    		cin>>n;
    		if(n == 1){
    			cout<<1<<endl<<1<<endl;
    			continue;
    		}
    		for(int i = 31; i; i -- ){  //从高位向低位遍历 
    			if(n >> i & 1){  找到第一个1,这是要个构建的基础组数(一组两个数,如21、43、65) 
    				len = i;
    				break;
    			}
    		}
    		
    		cur = 2 * len;
    	//	cout<
    		for(int i = 0; i < len; i ++ ){
    			if(n >> i & 1){  //如果遇到要补的1
    				ans[ ++ cnt] = ++ cur;
    				int x = i + 1;
    				while((1 << x) <= n && !((n >> x) & 1)){  //这里循环的次数就是i到更高位1之间0的数量+1 
    					ans[ ++ cnt] = ++ cur;
    					x ++ ; 
    				//	cout<<"x:"<
    				}
    			}
    			ans[ ++ cnt] = 2 * i + 2;
    			ans[ ++ cnt] = 2 * i + 1;
    		}
    		
    		cout<<cnt<<endl;
    		for(int i = 1; i <= cnt; i ++ ){
    			cout<<ans[i]<<' ';
    		}
    		cout<<endl;
    	}
    	return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
  • 相关阅读:
    few shot learnning笔记
    electronjs入门-聊天应用程序,与Electron.js通信
    把飞书云文档变成HTML邮件:问题挑战与解决历程
    uvm环境获取系统时间的方法和使用案例
    java面试题总结3
    Ubuntu 安装 docker
    Navicat 强大的数据模型功能 | 面向数据库设计、架构和数据资产梳理等使用场景
    【超详细】CentOS 7安装MySQL 5.7【安装及密码配置、字符集配置、远程连接配置】
    Union和union导致的数据不一致
    linux 设置在连续输错错误密码n次后,锁定该用户
  • 原文地址:https://blog.csdn.net/qiaodxs/article/details/126685999