给定一个长度为的字符串。
输出中出现正好一次的字母(任意,如abc
中,三个字母都可为答案)。
如果没有,输出-1
。
数据保证的长为,且由小写英文字母组成。
输出任意符合条件的答案。
输出 | |
---|---|
pop |
o |
abc |
a /b /c |
xxx |
-1 |
我们设输入的3个字母分别为a
、b
、c
。
首先,如果,那么输出。
其次,我们依次尝试找到两个相同的字母:
xxy
形式():输出xyx
形式():输出yxx
形式():输出xyz
形式():输出任意一个这里,我把最后两种情况合并了(一个else
搞定,都输出):
#include <cstdio>
using namespace std;
int main()
{
char a = getchar(), b = getchar(), c = getchar();
if(a == b && b == c) puts("-1");
else if(a == c) putchar(b);
else if(a == b) putchar(c);
else putchar(a);
return 0;
}
个员工参加了一场选聘考试。
第个员工数学考了分,英语分。
公司按如下的方式选聘员工:
注意:分数相同的员工按编号排序。
输出被录取的所有员工的编号,按升序排列。
输出被录取的所有员工的编号,按升序排列,每行一个。
略,请自行前往AtCoder查看
本题主要有两种思路:
pair<int, int>
代表一个员工,再使用vector
+sort
或priority_queue
执行三次分别排序数学、英语、总分;struct { int math, english, id; }
表示员工,存储一次,排序三次(使用不同的排序依据)详见代码1、代码2。
vector
+sort
实现#include <cstdio>
#include <vector>
#include <algorithm>
#define maxn 1005
using namespace std;
int a[maxn], b[maxn];
bool used[maxn];
int main()
{
int n, x, y, z;
scanf("%d%d%d%d", &n, &x, &y, &z);
for(int i=0; i<n; i++)
scanf("%d", a + i);
for(int i=0; i<n; i++)
scanf("%d", b + i);
// Math
vector<pair<int, int>> sel_a;
for(int i=0; i<n; i++)
sel_a.emplace_back(-a[i], i);
sort(sel_a.begin(), sel_a.end());
for(int i=0; i<x; i++)
used[sel_a[i].second] = true;
// English
vector<pair<int, int>> sel_b;
for(int i=0; i<n; i++)
if(!used[i])
sel_b.emplace_back(-b[i], i);
sort(sel_b.begin(), sel_b.end());
for(int i=0; i<y; i++)
used[sel_b[i].second] = true;
// Total
vector<pair<int, int>> sel_t;
for(int i=0; i<n; i++)
if(!used[i])
sel_t.emplace_back(-(a[i] + b[i]), i);
sort(sel_t.begin(), sel_t.end());
for(int i=0; i<z; i++)
used[sel_t[i].second] = true;
for(int i=0; i<n; i++)
if(used[i])
printf("%d\n", i + 1);
return 0;
}
priority_queue
实现#include <cstdio>
#include <queue>
#define maxn 1005
using namespace std;
int a[maxn], b[maxn], c[maxn];
bool used[maxn];
inline void selectOnce(int* scores, int n, int snum)
{
priority_queue<pair<int, int>> sel;
for(int i=0; i<n; i++)
if(!used[i])
{
sel.emplace(-scores[i], i);
if(sel.size() > snum) sel.pop();
}
while(!sel.empty())
used[sel.top().second] = true, sel.pop();
}
int main()
{
int n, x, y, z;
scanf("%d%d%d%d", &n, &x, &y, &z);
for(int i=0; i<n; i++)
scanf("%d", a + i);
for(int i=0; i<n; i++)
scanf("%d", b + i);
for(int i=0; i<n; i++)
c[i] = a[i] + b[i];
selectOnce(a, n, x);
selectOnce(b, n, y);
selectOnce(c, n, z);
for(int i=0; i<n; i++)
if(used[i])
printf("%d\n", i + 1);
return 0;
}
#include <cstdio>
#include <vector>
#include <algorithm>
#define maxn 1005
using namespace std;
struct Emp { // Employee
int math, eng, id;
} emps[maxn];
inline bool cmp1(const Emp& e1, const Emp& e2) {
return e1.math == e2.math?
e1.id < e2.id:
e1.math > e2.math;
}
inline bool cmp2(const Emp& e1, const Emp& e2) {
return e1.eng == e2.eng?
e1.id < e2.id:
e1.eng > e2.eng;
}
inline bool cmp3(const Emp& e1, const Emp& e2) {
int tot1 = e1.math + e1.eng, tot2 = e2.eng + e2.math;
return tot1 == tot2?
e1.id < e2.id:
tot1 > tot2;
}
inline bool cmp4(const Emp& e1, const Emp& e2) {
return e1.id < e2.id;
}
int main()
{
// Input
int n, x, y, z;
scanf("%d%d%d%d", &n, &x, &y, &z);
for(int i=0; i<n; i++)
scanf("%d", &emps[i].math),
emps[i].id = i;
for(int i=0; i<n; i++)
scanf("%d", &emps[i].eng);
// Sort
auto last = emps + n;
sort(emps, last, cmp1);
sort(emps + x, last, cmp2);
sort(emps + x + y, last, cmp3);
sort(emps, emps + x + y + z, cmp4); // 按编号升序排序
// Output
for(int i=0; i<x+y+z; i++)
printf("%d\n", emps[i].id + 1);
return 0;
}
Takahashi有一个级的红色宝石。
他可以重复下列操作任意次数:
Takahashi最后最多能得到几个级的蓝色宝石?
输出一个整数,即最终蓝色宝石的数量。
输出 | |||
---|---|---|---|
注意小心位整数(int/int32
)溢出。
要获得级的蓝宝石,必须先尽可能多的获得级的蓝宝石。
而要达到这个目的,就需要有尽可能多的级红宝石。
以此类推,我们可以按顺序进行操作,操作……直到所有宝石全部为级(也就是循环次)。维护两个变量(初始为)和(初始为),分别表示当前的红、蓝宝石的数目。
每次循环,先将加上(操作),再将加上、乘上(操作)。
时间复杂度,如有读不懂的地方,可参考代码。
注意使用long long
。
#include <cstdio>
using namespace std;
int main()
{
int n, x, y;
scanf("%d%d%d", &n, &x, &y);
long long red = 1LL, blue = 0LL;
while(--n)
{
blue += red * x;
red += blue, blue *= y;
}
printf("%lld\n", blue);
return 0;
}
有张牌,上面分别写着数字。
按照这个顺序,我们进行个操作,第个操作的具体步骤如下:
求每张牌被吃掉的时间(若没有被吃掉,输出-1
,详见输出格式)。
是的一种排列。
输出行,第行表示卡片被吃掉的时间(如果没被吃掉,输出-1
)。
略,就是懒
首先肯定不能用vector<stack<int>>
这种数据结构,效率太低,容易写错,还不好用。可以用一个类似于并查集的数据结构,每次叠放操作都可看作“把下面的牌的父亲设置为上面的牌”。我们还需要记录并查集中每个连通分量的大小,方便模拟“吃掉”操作。
最终对于每个节点,输出其祖宗被吃掉的时间(咋听起来有点怪)。
目前的时间复杂度是,因为每次操作都需要用的时间,找到最小的符合条件的牌堆。
很容易想到,可以使用set
优化。
set
是自动排序的集合,常用的的操作有插入(insert
)、删除(erase
)、二分查找(lower_bound
/upper_bound
),一次操作的时间复杂度均为。
这时,使用一个set<int>
维护每个堆顶的卡牌编号,就可以把时间复杂度降到以内。
至此,此题完。注意对的特判。
#include <cstdio>
#include <set>
#define maxn 200005
using namespace std;
int fa[maxn], eat[maxn], sz[maxn];
int find(int x) {
return fa[x] == x? x: fa[x] = find(fa[x]);
}
int main()
{
int n, k;
scanf("%d%d", &n, &k);
set<int> cards;
for(int i=0; i<n; i++)
{
int x;
scanf("%d", &x);
x --;
eat[x] = -1, fa[x] = x;
if(k == 1)
{
eat[x] = i + 1;
continue;
}
auto it = cards.upper_bound(x);
if(it == cards.end())
cards.insert(x), sz[x] = 1;
else
{
fa[*it] = x;
cards.erase(it);
if((sz[x] = sz[*it] + 1) == k)
eat[x] = i + 1;
else cards.insert(x);
}
}
for(int i=0; i<n; i++)
printf("%d\n", eat[find(i)]);
return 0;
}
给定整数和对整数:。
题目保证对于任意,。
符合如下条件的整数序列被称作好的序列:
令长为的好序列的个数。求。
输出一行,即,用空格分隔。
略,请自行前往AtCoder查看
首先,根据题意,可被表示为一个区间,其中。
当对于每个,或时,区间符合条件。
若按这样直接暴力枚举,时间复杂度为,明显超时,不可取。
仔细想想会发现,对于两个区间和,若,且符合条件,则也肯定符合条件。
此时,可以考虑使用滑动窗口优化,则时间复杂度降至。
继续优化。在窗口滑动的过程中,每次移动左/右端点时考虑一次移动对当前符合条件的的数量的贡献,需要两个数组(记录每个和符合条件的个数)和(预处理每个数值对应的所有元素下标)。
总时间复杂度为,详见代码。
#include <cstdio>
#include <vector>
#define maxn 200005
using namespace std;
vector<int> inv[maxn];
int cnt[maxn], ans[maxn];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i=0; i<n; i++)
{
int a, b;
scanf("%d%d", &a, &b);
inv[a].push_back(i);
inv[b].push_back(i);
}
int left = n;
for(int i=1, j=1; i<=m; i++)
{
for(; j <= m && left > 0; j++)
for(int x: inv[j])
if(++cnt[x] == 1)
left --;
if(left > 0) break;
ans[j - i] ++, ans[m - i + 2] --;
for(int x: inv[i])
if(--cnt[x] == 0)
left ++;
}
for(int i=1; i<=m; i++)
printf("%d ", ans[i] += ans[i - 1]);
return 0;
}
给定一个二分图,形如下:
其中顶点集中的顶点数为,的顶点数为,总边数为(第条边连接和)。
请找出此图中任意长为的环。如果没有,输出-1
。
如果有长为的环,输出其中四个顶点的编号(顺序随意,用空格分隔)。
如果没有,输出-1
。
略,请自行前往AtCoder查看
注意到样例中只有,可以接受。
然后因为是二分图,所以长为的环肯定是在两个顶点集中各有两个点。
令为目前发现的与点都相连的点,初始化为(表示未发现)。
输入使用邻接表存储,存储连到的所有点,注意只需存顶点集的即可。
再对于每个,依次枚举中的两个点,如果,则执行,如果不是,则输出{x} {y} {v} {f(x,y)}
,结束程序。
时间复杂度约为。
本题中的时间复杂度怎么算?
中不同的组合只有种。
根据鸽笼原理(又称抽屉原理),在最坏情况下,种组合都记录过之后,下一种组合无论是什么肯定都已经记录过,因此最坏时间复杂度为,对于随机数据的平均时间复杂度远远小于这个值。
#include <cstdio>
#include <cstring>
#include <vector>
#define maxs 300005
#define maxt 3005
using namespace std;
vector<int> G[maxs];
int f[maxt][maxt];
int main()
{
int s, t, m;
scanf("%d%d%d", &s, &t, &m);
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
G[--u].push_back(--v - s);
}
memset(f, -1, sizeof(f));
for(int i=0; i<s; i++)
for(int j=0; j+1<G[i].size(); j++)
for(int k=j+1; k<G[i].size(); k++)
{
int u = G[i][j], v = G[i][k];
if(u > v) u ^= v ^= u ^= v;
if(f[u][v] != -1)
{
printf("%d %d %d %d\n", f[u][v] + 1, i + 1, u + s + 1, v + s + 1);
return 0;
}
f[u][v] = i;
}
puts("-1");
return 0;
}