1.
你有一个序列,现在你要支持几种操作:
-
insert x y
,在从前往后的第x个元素后面插入y这个数。如果x=0,那么就在开头插入。 -
delete x
,删除从前往后的第x个元素。 -
query k
,询问从前往后数第k个元素是多少。
输入格式
第一行一个整数m,表示操作个数。
接下来m行,每行一个上面所述的操作。
输出格式
输出若干行,对于每个查询操作,输出答案。
本题知道vector函数的基本用法即可
#include<iostream>
#include<vector>
#include<string>
using namespace std;
vector<int> v;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int m;
cin >> m;
while (m--) {
string str;
cin >> str;
if (str == "insert") {
int a, b;
cin >> a >> b;
auto ite = v.begin();
while (a--)ite++;
v.insert(ite, b);
}
else if (str == "query") {
int a;
cin >> a;
auto ite = v.begin();
while (--a)ite++;
cout << *ite << '\n';
}
else if (str == "delete") {
int a;
cin >> a;
auto ite = v.begin();
while (--a)ite++;
v.erase(ite);
}
}
return 0;
}
2.
给定一个长度为 n 的数组 a1,a2,…,an,接下来进行 n−1次操作。每次选择一个下标 x 将 ax�和 ax+1 合并成 ax×ax+1mod1000003 ,并且你会获得 (ax−ax+1)2 的分数。
所以每次操作后,数组的长度将会减 11,当最后只剩下一个元素时停止操作。输出最终能获得的最大分数。
输入格式
第一行一个数字 n。
接下来一行 n 个整数 a1,a2,…,an。
输出格式
一个数,表示答案。
本题我们可以先用一个二维数组预处理下各个区间的乘积和,s[i] [j]的意思是ai到aj的乘积。用一个二维动规数组f来做区间dp,dp[i] [j]意思是合并i到j可以得到的最大分数。然后我们从长度2开始枚举区间长度,来获得每个区间所能得到的最大分数。最后答案就是dp[1] [n]。
状态转移方程为
f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j] + (s[i][k]-s[k+1][j])* (s[i][k] - s[k + 1][j]));
下面附上代码
#include<iostream>
using namespace std;
const int N = 1010, MOD = 1000003;
long long f[N][N], s[N][N],v[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> v[i];
}
for (int i = 1; i <= n; i++)
{
s[i][i] = 1;
s[i][i-1] = 1;
for (int j = i; j <= n; j++)
{
s[i][j] = (v[j] * s[i][j - 1]) % MOD;
}
}
for (int len = 2; len <= n; len++)
{
for (int i = 1; i + len - 1 <= n; i++)
{
int j = i + len - 1;
for (int k = i; k < j; k++)
{
f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j] + (s[i][k]-s[k+1][j])* (s[i][k] - s[k + 1][j]));
}
}
}
cout << f[1][n] << endl;
return 0;
3.
对于本题,如果两个数通过乘上多个2和3可以达到相等,那么反过来,这两个数通过除去多个2和3也可以达到相等。而且这个相除后得到的数应该是这两个数之间的最大公约数。
那么这题就很好写了,我们只要求出来n个数之间的最大公约数,然后把这n个数除去多个2或3(整除),判断这些数能不能变成最大公约数,要是不行,说明这n个数不能达到完全相同,输出no。如果没有,那就输出yes。
#include<iostream>
using namespace std;
int gcd(int a, int b)
{
if (a % b == 0)return b;
return gcd(b, a % b);
}
bool dfs(int num,int k)
{
if (num < k)return false;
bool a = false, b = false;
if (num % 2 == 0)a = dfs(num / 2, k);
if (num % 3 == 0)b = dfs(num / 3, k);
return a || b || num == k;
}
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
vector<int>v(n);
for (int i = 0; i < n; i++)cin >> v[i];
int res = v[0];
for (int i = 1; i < n; i++)
{
res = gcd(v[i], res);
}
bool flag = true;
for (int i = 0; i < n; i++)
{
if (v[i]!=res&&!dfs(v[i], res))
{
cout << "NO" << endl;
flag = false;
break;
}
}
if (flag)cout << "YES" << endl;
}
return 0;
}
4.
对于一个字符串 S ,我们定义 f(S) 为 S 中出现的不同的字符个数。 例如 f(aba)=2,f(abc)=3,f(aaa)=1。
现在给定一个字符串 S (假设长度为 len),请你计算 ∑i=0len−1∑j=ilen−1f(S[i:j]) 。
输入格式
输入一行包含一个由小写字母组成的字符串 S 。
输出格式
输出一个整数表示答案。
对于本题,我们可以发现,对于i=1以及i=2两种情况,当i=2的字串第一次出现s[1]时,从此往后,i=2的字串与i=1的字串的f(s)相同,发现了这个规律后,本题就非常简单了。
#include <iostream>
#include <string.h>
#include <cstring>
using namespace std;
const int N = 1e6 + 5;
string s;
long long a[28], b[N], c, x, y, z, sum;
long long ans;
int main()
{
cin >> s;
c = s.length();
for (int i = 0; i < c; i++)
{
if (a[s[i] - 96] == 0)
{
sum++;
a[s[i] - 96] = 1;
}
ans += sum;
}
b[0] = ans;
for (int i = 1; i < c; i++)
{
b[i] = b[i - 1] - 1;
for (int j = i; j < c; j++)
{
if (s[j] != s[i-1])
b[i]--;
else
break;
}
ans += b[i];
}
cout << ans;
return 0;
}
5.
便利蜂的货架上摆了一排蒟蒻果冻,搞得鶸尛鱻眼花缭乱......
对于每个果冻,都有一个价格 w 和口感 t。鶸尛鱻有一个购物篮子,在挑选蒟蒻果冻的时候,他有以下几种操作:
- 操作 11:把一个价格为 w,口感为 t 的果冻放入篮子。
- 操作 22:拿出篮子中 最为廉价 的果冻。
- 操作 33:拿出篮子中 口感最差 的果冻。(t 越小,口感越差)
鶸尛鱻不喜欢重复,当操作 11 的 价格或口感 与篮中已有果冻重复时,他会立刻将其放回货架。
经过 n 次操作后,鶸尛鱻确定了要购买的若干果冻,请你帮他求出篮子里果冻的总价格。
输入格式
第 11 行一个正整数 n,代表操作次数。
第 22 行至第 (n+1) 行,每行 一个或三个 整数,分别表示 o,w,t。
w 和 t 当且仅当 op=1 时存在。
输出格式
输出一个整数,表示篮子里果冻的总价格。
本题掌握map的基本用法即可
#include<iostream>
#include<map>
using namespace std;
int main() {
int n,ans=0;
cin >> n;
map<int, int>p, t;
while (n--)
{
int st;
cin >> st;
if (st == 1)
{
int a, b;
cin >> a >> b;
if (p.count(a) == 0 && t.count(b) == 0)
{
p[a] = b;
t[b] = a;
}
}
else if (st == 2)
{
t.erase(p.begin()->second);
p.erase(p.begin());
}
else if (st == 3)
{
p.erase(t.begin()->second);
t.erase(t.begin());
}
}
ll sum = 0;
for (auto i : p)sum += i.first;
cout << sum << endl;
return 0;
}
6.
有n个玩家参加比赛,他们分别有能力值a1,a2,…,an。
需要进行n−1轮比赛,每一轮在剩下的玩家里任选两个玩家i,j。如果|ai−aj|>K,那么其中能力值高的玩家会获胜,能力值低的玩家会被淘汰。如果|ai−aj|≤K,那么两个玩家都有可能获胜,另一个玩家被淘汰。
n−1轮比赛之后,只剩下一个玩家。问有多少个玩家可能是最后获胜的玩家。
输入格式
第一行,两个整数n,K,表示玩家的总人数,和获胜条件中的参数。
接下来一行n个整数a1,a2,…,an,表示玩家的能力值。
输出格式
一个整数,表示最后可能获胜的玩家个数。
本题我们从能力值由大到小依次遍历即可,如果排序后第i个玩家的能力值减去第i-1个玩家的大于k,那么对于这i-1个玩家来说是不可能获胜的。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int a[N], n, k, ans=1;
int main()
{
cin >> n >> k;
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a+1, a + n + 1);
for (int i = n; i >= 2; i--)
{
if (a[i] - a[i - 1] <=k)
ans++;
else
break;
}
cout << ans;
}
7.
请按字典序从小到大的顺序输出所有序列,满足序列中有 p1 个 1, p2个 2, ……, pn 个 n。
输入格式
第一行一个整数 n。
第二行 n个整数 p1,p2,…,pn。
输出格式
按字典序从小到大的顺序一行一行输出所有满足条件的序列,每行一个序列,相邻两个数字需要用空格隔开。
对于本题,使用递归即可
#include <iostream>
using namespace std;
int n,a[10],c,p[100],sum;
void dfs(int x)
{
if (x == sum + 1)
{
for (int i = 1; i <= c; i++)
cout << p[i] << " ";
cout << '\n';
}
else
{
for (int i = 1; i <=; i++)
{
if (a[i] != 0)
{
a[i]--;
c++;
p[c] = i;
dfs(x + 1);
c--;
a[i]++;
}
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum += a[i];
}
dfs(1);
return 0;
}
8.
本题了解各进制之间的转换即可,注意开longlong
#include <iostream>
#include <string.h>
using namespace std;
long long n, m, x,a[10000],sum;
string s;
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
long long p = 1;
cin >> x >> s;
for (int i = s.length()-1; i >=0; i--)
{
if (s[i] >= 'A' && s[i] <= 'Z')
sum += p * (s[i] - 55);
else if (s[i] >= 'a' && s[i] <= 'z')
sum += p * (s[i] - 61);
else
sum += p * (s[i] - 48);
p *= x;
}
}
int q = 1;
a[q] = sum;
while (1)
{
a[q + 1] = a[q] / m;
a[q] = a[q] % m;
q++;
if (a[q] < m)
{
break;
}
}
for (int i = q; i >= 1; i--)
{
if (a[i] >= 10 && a[i] <= 35)
{
char f = a[i] + 55;
cout << f;
}
else if (a[i] >= 36 && a[i] <= 61)
{
char f = a[i] + 61;
cout << f;
}
else
cout << a[i];
}
return 0;
}
9.
题目描述
一个字符串S是另一字符串T的循环子串当且仅当存在k, T所有字符循环右移k位后得到的新串T′,满足S是T′的子串。
例如: abc
是 cefab
的循环子串。 (cefab
循环右移22位得到abcef
, abc
是abcef
的子串)
一个串P是完全循环串当且仅当对于它的任一子串H, 都有Hreverse是P的循环子串 (Hreverse 为 H的倒转, 如abc
reverse后 为cba
)。
给一个长度为n的字符串, 判断它是不是完全循环串。
输入格式
第一行一个正整数t, 表示测试数据组数。
对于每一组数据,第一行一个正整数n, 表示字符串的长度。接下来一行一个长度为n的字符串. 仅包含小写字母。
输出格式
对于每组测试数据,如果这个串是完全循环串, 输出YES
,否则输出NO
。每组测试数据之间输出换行。
数据范围
对于所有数据 有 1≤t≤100, 1≤n≤1e3, ∑n≤1e3。
本题的意思为字符串s的所有子串翻过来之后(abc变cba)能在s里找到一样的字符串,这个s可以是向右移动k次之后的样子(向右移动k次相当于把前面k个字符移动到末尾,比如abcde移动2次,那就变成cdeab)。
那么我们只要枚举所有可能的子串,然后在旋转后的字符串s里找看是不是都能找到即可,如果都能找到那就输出yes,一个找不到都要输出no。可是每次都要移动字符串s太麻烦了,所以我们要对字符串做点改变。
就像我们前面说的,移动k次是把前k个字符删除移动到末尾,那么最多移动len(字符串长度)次,字符串就会变回原样,那我们直接把一个相同的字符串s接到s后面即可,这样就可以在这个字符串里找到所有可能移动过后的字符串s,我们在这里面找子串即可。
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
string s;
cin >> n >> s;
bool flag = true;
string str = s + s;
for (int len = 2; len <= n; len++)
{
for (int i = 0; i + len <= n; i++)
{
string res = s.substr(i, len);
reverse(res.begin(), res.end());
if (str.find(res) == str.npos)
{
cout << "NO" << endl;
flag = false;
break;
}
}
if (!flag)break;
}
if (flag)cout << "YES" << endl;
}
return 0;
}
10.
故事接着《饿饿 饭饭 2》,又过了几个月,暑假来啦!!!
这天,cc和他的小伙伴们决定一起去游乐园玩,他们一天将游乐园的所有设施玩了个遍,甚至大摆锤,过山车他们还去了很多次,愉快的时间总是很短暂的,很快时间就来到了晚上,但是你以为一天的娱乐时光就这样结束了吗,那你就猜错啦。
晚上,游乐园晚上的party就开始啦,其中有一个游戏环节,赢的人可以得到免费的西瓜,饿到不行的cc和他的小伙伴非常希望得到这个西瓜。
包括cc和他的小伙伴,有t个玩家参与了这个游戏,每个玩家都有一张带有数字的卡片。第i�张卡片上有ni个数字,分别是m1,m2,...mn。
游戏过程中,主持人从袋子里一个一个地取出编号的球。 他用洪亮而清晰的声音大声念出球的编号,然后把球收起来。 如果玩家的卡片上有对应的数字,就可以将它划掉。 最先从他的卡片上划掉所有数字的人获胜。 如果多人同时从他们的卡片上划掉所有数字,那么这些人都不能赢得比赛。 在游戏开始时,袋子里有 100 个球,编号从 1 到 100,所有球的编号都是不同的。
cc偷偷知道了每个玩家的数字。 想请你确定每个玩家是否可以在最有利于他的情况下赢得比赛。
输入格式
第一行给出一个数t,代表t个玩家。
接下来第二行到t+1行,每行第一个数为ni,代表这个人手中有n个卡片,接下来给出序列a1...an表示这个人所拥有的卡片的数字。
输出格式
输出t行,每一行给出第i个人在最有利的情况下是否能赢得比赛,可以输出YES
, 不可以输出NO
。
数据范围
1≤t≤100,1≤n≤100,1≤mi≤100