第六周题解

本文介绍了10道算法题目,包括最长公共子序列、可爱序列、漂亮数、真假字符串等,提供了详细的解题思路和部分代码实现。题目涉及动态规划、二分查找、树状数组等算法,适合算法学习和练习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.最长公共子序列

给出从 1 到 n 的两个排列 P1 和 P2,求它们的最长公共子序列。

输入格式

第一行是一个正整数 n。

接下来两行,每行为 n 个数,为自然数 1,2,…,n 的一个排列。

输出格式

一个数,即最长公共子序列的长度。

数据范围

1≤n≤10^5

输入样例

5 
3 2 1 4 5
2 1 3 4 5

输出样例

4

因为给出的是一个1~n的排列,那么我们可以先将一号序列进行从小到大的排序,那么我们再将二号序列中的对应标号的元素也进行相应的排列,那么我们最后要求的结果其实也就是二号序列中的最长上升子序列(因为一号序列是单调递增的,二号序列中的元素要想和一号序列中的元素公共,那么这个子序列必然是上升的)

那么我们就可以用二分的方法很快得出答案了,完整代码如下:

#include<bits/stdc++.h>
using namespace std;
#define N 100005
int num1[N],num2[N],place[N];
vector<int>p;
bool check(int num){
    if(p.empty()){
        return true;
    }
    else{
        if(num>p[p.size()-1]){
            return true;
        }
    }
    return false;
}
int main(){
    int n,x;
    cin>>n;
    for(int i=1;i<=2;i++){
        for(int j=1;j<=n;j++){
            if(i==1){
                cin>>x;
                num1[j]=j;
                place[x]=j;
            }
            else{
                cin>>num2[j];
                num2[j]=place[num2[j]];
            }
        }
    }
    for(int j=1;j<=n;j++){
        if(check(num2[j])){
            p.push_back(num2[j]);
        }
        else{
            int point=lower_bound(p.begin(),p.end(),num2[j])-p.begin();
            p[point]=num2[j];
        }
    }
    cout<<p.size();
    return 0;
}

2.喵喵序列

题目描述

给定一个含有 n 个整数的序列 a_1,a_2,…a_n,三个数 i,j,k 是可爱的当且仅当 i<j<k 且 a_i<a_j<a_k。

请你求出有多少组 i,j,k 是可爱的。

输入格式

第 1 行一个整数 n 表示序列元素个数。

第 2 行 n 个整数分别表示 a_1,a_2,…a_n。

输出格式

一行一个整数,表示所求数量。

样例输入

5
1 2 2 3 4

样例输出

7

样例说明

满足条件的有:(1,2,3),(1,2,4),(1,2,3),(1,2,4),(1,3,4),(2,3,4),(2,3,4),共 7 个。

数据范围

对于全部数据,有 1≤n≤3×104,0≤a_i<263

这道题我就直接引用这位大佬的题解吧,我是真的不会树状数组(qwq),我一开始还在傻傻的直接dp,但是显然这道题显然直接dp是不可能过的,必须要优化,所以就直接看题解吧。qwq

完整代码如下:

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,l,r) for(int i=(l);i>=(r);i--)
#define ll long long
#define mset(s,t) memset(s,t,sizeof(t))
#define mcpy(s,t) memcpy(s,t,sizeof(t))
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define mp make_pair

typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;   
typedef vector<ll> Vll;               
typedef vector<pair<int, int> > vpii;
typedef vector<pair<ll, ll> > vpll;                        

const ll mod = 1e9 + 7;
//const ll mod = 998244353;
const double pi  = acos(-1.0);
inline ll ksc(ll x,ll y,ll mod)
{
    ll ans = 0;
    while (y) {
    	if (y & 1)
    		ans = (ans + x) %mod;
    	y >>= 1;
    	x = (x + x) %mod;
	}
	return ans;
}
inline ll qmi (ll a, ll b) {
	ll ans = 1;
	while (b) {
		if (b & 1) ans = ans * a;
		a = a * a;
		b >>= 1;
	}
	return ans;
}
inline int read () {
	int x = 0, f = 0;
	char ch = getchar();
	while (!isdigit(ch)) f |= (ch=='-'),ch= getchar();
	while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
	return f?-x:x;
}
template<typename T> void print(T x) {
	if (x < 0) putchar('-'), x = -x;
	if (x >= 10) print(x/10);
	putchar(x % 10 + '0');
}
inline ll sub (ll a, ll b) {
	return ((a - b ) %mod + mod) %mod;
}
inline ll add (ll a, ll b) {
	return (a + b) %mod;
}
// inline ll inv (ll a) {
	// return qmi(a, mod - 2);
// }
#define int long long
const int N = 3e5 + 10;
int n;
ll a[N];
int s[N];
int m;
void add1 (int x, int num) {
	for (;x <= m; x += x &-x)
		s[x] += num;
}
ll query (int x) {
	ll ans = 0;
	for (;x; x -= x & -x)
		ans += s[x];
	
	return ans;
}
vector<int> alls;
int l[N], r[N];

void solve() {
	cin >> n;
	for (int i = 1; i<= n; i++)	{
		cin >> a[i];
		alls.pb(a[i]);
	}	
	sort(all(alls));
	alls.erase(unique(all(alls)), alls.end());
	for (int i =  1; i <= n; i ++) {
		a[i] = lower_bound(all(alls), a[i]) - alls.begin();
		a[i] ++;
	}

 	
	m = alls.size();
	for (int i = 1; i <= n; i ++)
	{
		l[i] = query(a[i] - 1);
		add1 (a[i], 1);
	}
	memset(s, 0, sizeof s);
	
	for (int i = n; i >= 1; i --) {
		r[i] = query(m) - query(a[i]);
		add1 (a[i], 1);
	}
	int ans = 0;
	for (int i = 1; i<= n; i ++)
		ans += l[i] * r[i];
	cout << ans << endl;
}
signed main () {
	// ios::sync_with_stdio(0),cin.tie(0), cout.tie(0);
    int t;
    t =1;
    //cin >> t;
    while (t --) solve();
    return 0;
}

3.漂亮数

有一个长度为 n 的数字 X,由 n 位数字组成,即 a_1,a_2,…a_n.,它们按从左到右的顺序组成一个十进制的数。

并且,你将得到一个数 k,需要你构造出一个最小的数 Y,对于每一位 i(1≤i≤m−k), 满足 b_i=b_(i+k),并且X≤Y。

输入描述

第一行给出两个数 n,k, 其中 (2≤n≤200000,1≤k<n)

第二行给出 X:a_1,a_2,…a_n

输出描述

第一行给出Y的长度 m。

输出最小的满足条件的数 Y:b_1,b_2,…b_m

输入样例

3 2
353

样例

3
353

题目意思是,我们需要构造出一个大于等于X的数,这个数满足对于每一位数,从k+1位数开始,每一位数要与往前数第k位数相同。

那么我们其实就知道了,其实我们需要构造的数其实就是前面的1~k位数,因为后面的数都是前面数的复制。

那么我们只需要找X的1~k位数,例如在样例中,我们只需要找353的前两位数,也就是35,我们只需要判断Y是353还是363那个符合条件就行了。

知道了思路,那么代码就可以很容易写出来了。

因为新学了,python所以就用python小编了一下(python代码是真的少qwq)

完整代码如下:

import math
a,b=map(int,input().split())
c=input()
d=""
if a<=b:
    print(b)
    print(c)
else:
    d=c[0:b]
    shu=math.ceil(a/b)
    d=d*shu
    d=d[0:a]
    if int(d)>=int(c):
        print(a)
        print(d)
    else:
        d=int(d[0:b])+1
        d=str(d)*shu
        d=d[0:a]
        print(a)
        print(d)

    

4.真假字符串

给定两个长度相等的字符串 S1,S2, 问能否找出一个字符串 S, 使得 S 只删除一个字符可以得到 S1, 并且 S 只删除一个字符也可以得到 S2 (可以是不同位置的字符)。

输入格式

输入第一行给出字符串 S1, 第二行给出字符串 S2, 两个字符串的长度 1≤len≤300000

输出格式

如果能找到满足条件的字符串 S, 输出 1, 否则输出 0。

样例输入

abacaa
aacaba

样例输出

1

样例解释

abacaba 删除第二个字符 b 可以得到字符串 S1, 并且删除第一个字符 b 可以得到字符串 S2。

我们可以发现,如果可以找到S,那么我们必须满足,对于S1与S2只允许有一个字符的差别。所以我们可以选择遍历两个字符串,找两个字符串不同的地方,在根据不同的情况来判断是否有可能找到S

说的可能有亿点点抽象,那么直接上代码:

a=input()
b=input()
point_a=0
point_b=0
ans=0
count_a=0
count_b=0
flag=0
if len(a)!=len(b):
    print(0)
elif len(a)==len(b)==1:
    print(1)
else:
    while point_a<len(a) and point_b<len(a):
        if a[point_a]!=b[point_b]:
            if count_a==1 and count_b==1:
                ans=1
                break
            if point_a!=len(a)-1 and len(b)!=len(a)-1 and count_a==0 and count_b==0 and a[point_a+1]==b[point_b+1]:
                point_a+=1
                point_b+=1
                count_a=1
                count_b=1
                flag=1
            if point_a!=len(a)-1:
                if a[point_a+1]==b[point_b]:
                    point_a+=1
                    count_a+=1
                    flag=1
                    continue
            if point_b!=len(a)-1:
                if b[point_b+1]==a[point_a]:
                    point_b+=1
                    count_b+=1
                    flag=1
                    continue
            if flag==0 and point_a!=len(a)-1 and point_b!=len(a)-1:
                ans=1
                break
        flag=0
        point_a+=1
        point_b+=1
    if ans==0:
        print(1)
    else:
        print(0)

c++写法与python相差不大


5.走不出的迷宫

有一个 H行 W列的迷宫(行号从上到下是 1−H,列号从左到右是 1−W),现在有一个由 .# 组成的 HW 列的矩阵表示这个迷宫的构造,. 代表可以通过的空地,# 代表不能通过的墙。

现在有个人从 起点 (1,1) 开始走,他每一步只能往右走一格或者往下走一格,并且他不能跨越迷宫的边界。他会一直走,直到没有可以走的路时停下来。

请问这个人最多可以经过多少个格子?

输入格式

第一行两个整数 H,W,表示迷宫有 H 行 W 列。

接下来一个 H 行 W 列的由 .# 组成的矩阵,表示迷宫的构造。

注意:保证 (1,1) 的位置一定是 .

输出格式

一个整数,表示最多步数。

样例输入

3 4
.#..
..#.
..##

样例输出1

4

样例输入2

1 1
.

样例输出2

1

样例输入3

5 5
.....
.....
.....
.....
.....

样例输出3

9

数据规模

对于全部数据保证 1≤H,W≤100

一道简单的搜素题,因为题目的数据规模比较小,所以可以直接写个dfs,如果大一点的话,可以使用动态规划。这里,我是使用了动态规划的,dfs其实也可以,并且会更好写一点(因为就只有向下和向右两个方向)

完整代码如下:

#include<iostream>
using namespace std;
#define N 105
char place[N][N];
int dp[N][N];
int main(){
    int hang,lie,maxx=1,ans1,ans2;
    cin>>hang>>lie;
    for(int i=1;i<=hang;i++){
        for(int j=1;j<=lie;j++){
            cin>>place[i][j];
        }
    }
    dp[1][1]=1;
    for(int j=1;j<=hang;j++){
        for(int i=1;i<=lie;i++){
            if(place[j][i]=='.'){
                if(dp[j-1][i]){
                    dp[j][i]=max(dp[j][i],dp[j-1][i]+1);
                }
                if(dp[j][i-1]){
                    dp[j][i]=max(dp[j][i],dp[j][i-1]+1);
                }
                maxx=max(maxx,dp[j][i]);
            }
        }
    }
    printf("%d",maxx);
    return 0;
}

6.最长同余子数组

给定一个 N 长数组 {A}, 元素之间 两两不同.

现在要求你找出最长的 连续子序列, 使得这些元素 (modM) 意义下同余, 其中 M≥2≥2.

形式化的说, 在数组中找到最长的 A[L…R],∃M≥2, 使得:

A_L≡A_(L+1)≡A_(L+2)≡⋯≡A_R(modM)

其中, a≡b(modM) 即是说 a%M=b%M

输出此长度即可.

数据规模

  • 1≤n≤2×10^3
  • 1≤a_i≤10^18

输入格式

第一行一个数字 N。

接下来一行 N 个整数 A_1,A_2,…,A_N,。

输出格式

一个数,表示最长连续同余子序列的长度。

样例输入

4
8 2 10 5

样例输出

3

注意到 8,2,10均为偶数.


bonus1: consider what if N is greater (even 1≤N≤2×1051≤≤2×10^5)?

bonus2: consider how to solve the ‘subsequence’ version.

因为我们要找的是同余子数列,那么我们其实可以知道那么这里的每个数可以表示成m个N的和再加上一个余数,那么我们就可以先求出相邻两个数之间的差,之后求这些差的gcd,如果他们同余,那么他们的gcd结果必定>1,那么我只需要枚举1~n-1的差,知道求得的gcd值为1,那么就可以更新最长的长度。

那么知道思路,完整代码就好写了。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 2005
int n;
ll nums[INF],cha[INF];

int main(){
    cin>>n;
ll ans;
for(int i=1;i<=n;i++){
    cin>>nums[i];
}
for(int i=2;i<=n;i++){
    cha[i-1]=abs(nums[i]-nums[i-1]);
}
int maxx=1,count=1;
for(int i=1;i<=n-2;i++){
ans=cha[i];
count=1;
     for(int j=i;j<=n-1;j++){
           ans=__gcd(ans,cha[j]);
           if(ans==1){
                maxx=max(maxx,count);
                count=1;
                break;
           }
           else{
                count++;
           }
     }
     maxx=max(maxx,count);
}
    printf("%d",maxx);
    return 0;
}

7.互质

题目描述

给你一个包含n个正整数的序列 A=(A_1,A_2,…,A_n)找到 [1,m]中每一个满足下列条件的 k:

gcd(A_i,k)=1, 1≤i≤n

输入描述

第一行输入两个整数 n, m 第二行输入n个整数代表序列A

输出描述

第一行输出一个整数代表满足条件的k的数量 接下里每一行输出一个整数,代表一个满足条件的k

样例输入

3 12
6 1 5

样例输出

3
1
7
11

数据范围

1≤n,m≤100000 1≤a_i≤100000

因为k对这些数是互质的,那么我们可以其实就是找到这些数的因子,那么这些因子的整数倍必定是不满足条件的。我们可以选择将这些数给筛掉。那么我们的就可以用一个数组来标记被删除掉的数,那么没有被删除的数也就是最后的结果,我们遍历输出即可。

这里可以用set数组来去重复的因子,方便筛选。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
#define N 100005
int nums[N],n,m;
bool book[N];
set<int>arr;
void findyi(int x){
    for(int i=2;i<=x/i;i++){
        while(x%i==0){
            arr.insert(i);
            x/=i;
        }
    }
    if(x>1){
        arr.insert(x);
    }
}
void sai(){
    for(auto i=arr.begin();i!=arr.end();i++){
        int t=*i;
        for(int j=1;j*t<=m;j++){
            book[j*t]=1;                
        }
    }
}
int main(){
    int num=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        scanf("%d",&nums[i]);
        if(nums[i]==1){
            continue;
        }
        findyi(nums[i]);
    }
    sai();
    for(int i=1;i<=m;i++){
        if(!book[i]){        
            num++;
        }
    }
    printf("%d\n",num);
    for(int i=1;i<=m;i++){
        if(!book[i]){ 
            printf("%d\n",i);
        }
    }
    return 0;
}

8.最短路计数

题目描述

给出一个 N 个顶点 M 条边的无向无权图。

问从顶点 1 开始,到其他每个点的最短路有几条。

输入格式

第 1 行包含两个正整数 N,M。

接下来 M 行,每行两个正整数 x,y, 表示存在一条由顶点 x 到顶点 y 的边。(可能存在重边和自环)

输出格式

输出 N 行,每行一个非负整数。

第 i 行输出从顶点 1 到顶点 i 的不同最短路个数。

由于数据可能很大,你只需要输出 ans mod 100003 的结果。

若顶点 1 不能到达顶点 i,请输出 0。

样例输入

5 7
1 2
1 3
2 4
3 4
2 3
4 5
4 5

样例输出

1
1
1
2
4

数据范围

1≤N≤106,1≤M≤2×106

提示

由于数据量较大,请使用较为快速的输入/输出方式。

因为这是个无向无权图,那么我们其实可以直接从开头开始进行一次bfs,那么我们其实可以知道如果1号节点到k号节点有最短路径数为 num_k,那么从1号节点到达比k号节点更深一层的节点的最短路径数也就是num_k+1,

也就是存在状态转移方程:
d e p t h [ t ] = d e p t h [ w ] + 1 depth[t]=depth[w]+1 depth[t]=depth[w]+1
那么我们就可以写一个从1号节点开始的bfs通过该状态转移方程来求出每个点的最短路径数

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
#define N 1000005
#define mod 100003
queue<int>Q;
int deep[N],cnt[N],book[N],n,m;
vector<int>G[N];
int main(){
	int t,temp,x,y;
	cin>>n>>m;
	for(int i=0;i<m;i++){
		cin>>x>>y;
		G[x].push_back(y);
		G[y].push_back(x);
	}
	Q.push(1);
	cnt[1]=1,book[1]=1,deep[1]=0;
	while(!Q.empty()){
		int temp=Q.front();
		Q.pop();
		for(int j=0;j<G[temp].size();j++){
			t=G[temp][j];
			if(!book[t]){
				book[t]=1,deep[t]=deep[temp]+1;
				Q.push(t);
			}
			if(deep[t]==deep[temp]+1){
				cnt[t]=(cnt[t]+cnt[temp])%mod;
			}
		}
	}
	for(int i=1;i<=n;i++){
		cout<<cnt[i]<<"\n";
	}
    return 0;
}

9.最后的舞会

老师为即将毕业的同学们准备了一场舞会,有2N个同学会参加这场舞会,他们会被分成N对跳舞,每个人有一个编号,如果编号为i的同学和编号为j的同学配对,那么这一对的满意度是Ai,j(i<j),(<),我们规定这个舞会的满意度为每一队的满意度的异或和,也就是说,当同学们分成N组后,第i对同学的满意度为A_i,那么舞会的满意度为A_1⊕A_2⊕…A_N

请你求出本场舞会满意度的最大值

输入描述

第一行给出一个数N,有2N个人参加舞会

接下来给出一个矩阵表示i和j配对时的满意度

A_1,2,A_1,3,…A_1,2N

A_2,3,…A_2,2N

… … …

A_(2N−1),2N

其中1≤N≤8,0≤A_i,j≤2^30

输出描述

输出本场舞会满意度的最大值

样例输入

2
4 0 1
5 3
2

样例输出

6

样例解释

如果{1,2},{3,4},ans=A_1,2⊕A_3,4=4⊕2=6

如果{1,3},{2,4},ans=A_1,3⊕A_2,4=0⊕3=3

如果{1,4},{2,3},ans=A_1,4⊕A_2,3=1⊕5=4

最后答案为max(6,3,4)=6

其实这道题的数据很少,那么我们其实可以写个dfs就可以解决了。

但是需要注意,我们还需要进行优化,也就是需要进行剪枝。

这样就可以轻松过了

完整代码如下:

#include <bits/stdc++.h>
using namespace std;
bool vis[20];
int a[20][20];
int ans, n;
void dfs(int step, int res) // step为当前已经匹配的对数,res是当前的异或和
{
    if (step == n) //已全部匹配更新答案
    {
        ans = max(ans, res);
        return;
    }
    int i;
    for (i = 2; i <= n + n - 1; i++)
    {
        if (!vis[i]) //找到1~2n中第一个未被匹配的数(剪枝)
            break;
    }
    for (int j = i + 1; j <= n + n; j++)
    {
        if (vis[j] == 0) //未被匹配
        {
            vis[i] = 1, vis[j] = 1; //匹配
            dfs(step + 1, res ^ a[i][j]);
            vis[i] = 0, vis[j] = 0; //取消标记
        }
    }
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n + n - 1; i++)
        for (int j = i + 1; j <= n + n; j++)
            cin >> a[i][j];
    vis[1] = 1;                      // 1肯定会被匹配,直接加入匹配(剪枝)
    for (int i = 2; i <= n + n; i++) //枚举与1匹配的位置
    {
        vis[i] = 1;
        dfs(1, a[1][i]);
        vis[i] = 0;
    }
    cout << ans;
    return 0;
}

10.倒数第n个字符串

给定一个完全由小写英文字母组成的字符串等差递增序列,该序列中的每个字符串的长度固定为 L,从 L 个 a 开始,以 11 为步长递增。例如当 L 为 33 时,序列为 aaa,aab,aac,…,aaz,aba,abb,…,abz,…,zzz。这个序列的倒数第 2 个字符串就是 zzy。对于任意给定的 L,本题要求你给出对应序列倒数第 N 个字符串。

输入格式

输入在一行中给出两个正整数 L (1≤L≤6))和 N( N≤10^5). 注意:数据范围有修改!!!

输出格式

在一行中输出对应序列倒数第 N 个字符串。题目保证这个字符串是存在的。

样例输入

6 789

样例输出

zzzyvr

这道题如果你不清楚,那么我们就反过来想,他是求正数的第n个字符串(这个n需要进行再次计算),那么我们其实就能发现其实这就是一个进制问题,因为字母有26个,所以是一个26进制的问题。知道了这个,那么我们就可以按照进制问题解决这个问题了

完整代码如下:

#include <bits/stdc++.h>
using namespace std;    
char cnt[10];    
int l,n;
void turn(int num){
    if(num==0){
        for(int i=0;i<l;i++){
            cout<<"a";
        }
        return;
    }
    int yu,ans[10],len=0,l=0;
    while(num>0){
        yu=num%26;
        ans[len++]=yu;
        num/=26;
    }
    for(int i=len-1;i>=0;i--){
        cnt[l++]='a'+ans[i];
    }
    for(int i=0;i<l;i++){
        cout<<cnt[i];
    }
}
int main(){

    cin>>l>>n;
    int num=pow(26,l)-n;
    turn(num);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值