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),现在有一个由 .
和 #
组成的 H
行 W
列的矩阵表示这个迷宫的构造,.
代表可以通过的空地,#
代表不能通过的墙。
现在有个人从 起点 (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;
}