第
一
次
6
题
纪
念
第一次6题纪念
第一次6题纪念
A - Lexicographic Order
题意:
s
字
符
串
的
字
典
序
是
否
小
于
t
s字符串的字典序是否小于t
s字符串的字典序是否小于t
思路:
模
拟
模拟
模拟
时间复杂度:
O
1
O1
O1
#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int N = 1e6 + 10 , M = 2010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
signed main()
{
string s , t ;
cin >> s >> t ;
if(s < t) puts("Yes") ;
else puts("No") ;
return 0;
}
B - AtCoder Quiz
题意:
给
定
4
个
不
同
的
字
符
串
给定4个不同的字符串
给定4个不同的字符串
输
入
三
个
不
同
字
符
串
,
求
没
有
出
现
过
的
那
个
输入三个不同字符串,求没有出现过的那个
输入三个不同字符串,求没有出现过的那个
思路:
模
拟
模拟
模拟
时间复杂度:
O
1
O1
O1
#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int N = 1e6 + 10 , M = 2010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
map<string,int> q ;
signed main()
{
string x ;
q["ABC"] ++ ;
q["ARC"] ++ ;
q["AGC"] ++ ;
q["AHC"] ++ ;
for(int i = 1 ; i <= 3 ; i ++)
{
cin >> x ;
q[x] ++ ;
}
for(auto i : q)
{
if(i.y == 1)
{
cout << i.x << "\n" ;
break ;
}
}
return 0;
}
C - Inverse of Permutation
题意:
给
定
一
个
n
的
排
列
给定一个n的排列
给定一个n的排列
求
对
所
有
的
[
1
<
=
i
<
=
n
]
等
于
i
的
下
标
求对所有的[1 <= i <= n ] 等于i的下标
求对所有的[1<=i<=n]等于i的下标
思路:
模
拟
模拟
模拟
时间复杂度:
O
n
On
On
#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int N = 1e6 + 10 , M = 2010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
int n ;
int a[N] ;
int q[N] ;
signed main()
{
cin >> n ;
fer(i,1,n) sf(a[i]) ;
fer(i,1,n)
{
q[a[i]] = i ;
}
fer(i,1,n)
{
cout << q[i] << " " ;
}
return 0;
}
D - Cutting Woods
题意:
一
开
始
有
一
根
木
棒
一开始有一根木棒
一开始有一根木棒
长
度
为
n
长度为n
长度为n
从
1
到
n
编
号
从1到n编号
从1到n编号
输
入
q
个
操
作
输入q个操作
输入q个操作
操
作
1
操作1
操作1
断
开
x
所
在
的
木
棒
断开x所在的木棒
断开x所在的木棒
[
1
,
n
]
断
开
x
变
成
了
[
1
,
x
]
[
x
+
1
,
n
]
[1,n] 断开x变成了 [1,x] [x+1,n]
[1,n]断开x变成了[1,x][x+1,n]
操
作
2
操作2
操作2
询
问
x
所
在
的
区
间
长
度
询问x所在的区间长度
询问x所在的区间长度
q
<
=
1
e
5
q <= 1e5
q<=1e5
n
<
=
1
e
9
n <= 1e9
n<=1e9
1
<
=
x
<
=
1
e
9
1 <= x <= 1e9
1<=x<=1e9
思路:
有
个
很
重
要
的
性
质
有个很重要的性质
有个很重要的性质
分
割
之
后
左
边
以
及
右
边
这
个
区
间
的
答
案
是
固
定
的
分割之后左边以及右边这个区间的答案是固定的
分割之后左边以及右边这个区间的答案是固定的
也
就
是
说
答
案
只
跟
分
割
点
有
关
也就是说答案只跟分割点有关
也就是说答案只跟分割点有关
比
如
区
间
[
l
,
r
]
分
割
x
变
成
了
[
l
,
x
]
,
[
x
+
1
,
r
]
比如区间[l,r] 分割x变成了[l,x] , [x+1,r]
比如区间[l,r]分割x变成了[l,x],[x+1,r]
可
以
发
现
可以发现
可以发现
l
到
x
这
个
区
间
的
询
问
答
案
都
是
x
−
l
+
1
l到x这个区间的询问答案都是x-l+1
l到x这个区间的询问答案都是x−l+1
x
+
1
到
r
这
个
区
间
的
询
问
答
案
都
是
r
−
(
x
+
1
)
−
1
x+1到r这个区间的询问答案都是r-(x + 1) - 1
x+1到r这个区间的询问答案都是r−(x+1)−1
所
以
可
以
考
虑
二
分
找
到
所
在
区
间
相
邻
2
个
的
分
割
点
下
标
所以可以考虑二分找到所在区间相邻2个的分割点下标
所以可以考虑二分找到所在区间相邻2个的分割点下标
先
考
虑
边
界
问
题
先考虑边界问题
先考虑边界问题
边
界
无
非
是
0
−
n
+
1
/
0
−
n
/
1
−
n
/
1
−
n
+
1
边界无非是0 - n + 1 / 0 - n / 1 - n / 1 - n + 1
边界无非是0−n+1/0−n/1−n/1−n+1
这
4
种
的
其
中
一
种
这4种的其中一种
这4种的其中一种
这 个 时 候 先 假 设 区 间 是 [ 1 , 2 ] , [ 3 , 4 ] , [ 5 ] 这个时候先假设区间是[1,2] , [3,4] , [5] 这个时候先假设区间是[1,2],[3,4],[5]
分割点是 0/1 2 4 5/5+1
对于查询2
找到第一个大于等于2的数是2
第一个小于2的数应该是 0/1
答案是2
所以应该是 2 - 0 = 2
左边界应该是0
在考虑查询5
答案是1
找到第一个大于等于5的数是5/6
第一个小于5的数应该是4
所以应该是 5 - 4 = 1
右边界是n
在考虑二分操作的时候 分割点数组保持有序
所以可以用set动态维护
时间复杂度: O n l o g n Onlogn Onlogn
#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int N = 1e6 + 10 , M = 2010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
set<int> q ;
int n , t ;
signed main()
{
cin >> n >> t ;
q.insert(0) ;
q.insert(n) ;
while(t--)
{
int c , x ;
sf(c) , sf(x) ;
if(c == 1)
{
q.insert(x) ;
}
else
{
cout << *q.lower_bound(x) - *--q.lower_bound(x) << "\n" ;
}
}
return 0;
}
E - Sorting Queries
题意:
一
开
始
有
一
个
空
的
序
列
一开始有一个空的序列
一开始有一个空的序列
q
个
操
作
q个操作
q个操作
操
作
1
操作1
操作1
在
序
列
末
尾
添
加
一
个
数
x
在序列末尾添加一个数x
在序列末尾添加一个数x
操
作
2
操作2
操作2
输
出
序
列
第
一
个
数
并
且
删
除
输出序列第一个数并且删除
输出序列第一个数并且删除
操
作
3
操作3
操作3
给
当
前
序
列
排
序
给当前序列排序
给当前序列排序
q
<
=
1
e
5
q <= 1e5
q<=1e5
x
<
=
1
e
9
x <= 1e9
x<=1e9
思路:
假
设
不
考
虑
操
作
3
假设不考虑操作3
假设不考虑操作3
可
以
用
数
组
模
拟
队
列
o
n
解
决
可以用数组模拟队列on解决
可以用数组模拟队列on解决
那
么
队
列
是
一
定
要
使
用
的
那么队列是一定要使用的
那么队列是一定要使用的
现
在
考
虑
一
下
怎
么
维
护
操
作
3
对
队
列
的
影
响
现在考虑一下怎么维护操作3对队列的影响
现在考虑一下怎么维护操作3对队列的影响
队
列
的
2
个
元
素
队列的2个元素
队列的2个元素
h
h
代
表
对
头
hh代表对头
hh代表对头
t
t
代
表
队
尾
tt代表队尾
tt代表队尾
排
序
如
果
暴
力
的
话
O
n
2
l
o
g
n
排序如果暴力的话On^2logn
排序如果暴力的话On2logn
考
虑
如
何
优
化
考虑如何优化
考虑如何优化
有
一
个
很
重
要
的
性
质
是
排
序
的
时
候
有一个很重要的性质是排序的时候
有一个很重要的性质是排序的时候
是
边
插
入
边
排
序
是边插入边排序
是边插入边排序
所
以
可
以
用
s
e
t
动
态
维
护
所以可以用set动态维护
所以可以用set动态维护
然
后
把
对
头
指
向
s
e
t
后
面
的
第
一
个
下
标
然后把对头指向set后面的第一个下标
然后把对头指向set后面的第一个下标
时间复杂度:
O
n
l
o
g
n
Onlogn
Onlogn
#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int N = 1e6 + 10 , M = 2010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
int a[N] ;
multiset<int> q ;
// 带重复元素的set
bool st[N] ;
signed main()
{
int hh = 0 , tt = 0 ;
int t ;
cin >> t ;
int k = 1; // k = 1 表示队列第一个数的下标是1
int op ;
while(t--)
{
sf(op) ;
if(op == 1)
{
int x ;
sf(x) ;
a[++ tt] = x ;
// 队尾加入x
}
else if(op == 2)
{
// 如果排序过
if(q.size())
{
cout << *q.begin() << "\n" ;
q.erase(q.begin()) ;
// 输入对头并且删除
}
else
{
cout << a[k] << "\n" ;
st[k] = 1 ;
k ++ ;
// 没有排序过就输出第一个数
// 然后标记这个数没有了
// 对头 ++
}
}
else
{
for(int i = k ; i <= tt ; i ++)
{
if(!st[i]) q.insert(a[i]) ;
}
k = tt + 1 ;
// 插入之后 对头指向队尾的下一个下标
}
}
return 0;
}
G - Groups
题意:
有
1
,
2
,
3......
n
有1,2,3......n
有1,2,3......n
n
个
数
n个数
n个数
问
这
n
个
数
可
以
分
为
多
少
组
问这n个数可以分为多少组
问这n个数可以分为多少组
并
且
保
证
每
一
组
的
数
%
m
的
值
没
有
相
等
的
并且保证每一组的数\%m的值没有相等的
并且保证每一组的数%m的值没有相等的
输
出
每
个
组
的
方
案
输出每个组的方案
输出每个组的方案
n
<
=
5000
n <= 5000
n<=5000
思路:
假
设
先
不
考
虑
保
证
每
一
组
的
数
%
m
的
值
没
有
相
等
的
假设先不考虑保证每一组的数\%m的值没有相等的
假设先不考虑保证每一组的数%m的值没有相等的
那
么
就
是
一
个
很
经
典
的
第
二
类
斯
特
林
数
那么就是一个很经典的第二类斯特林数
那么就是一个很经典的第二类斯特林数
f
[
i
]
[
j
]
表
示
前
i
个
数
分
成
j
组
的
方
案
数
f[i][j]表示前i个数分成j组的方案数
f[i][j]表示前i个数分成j组的方案数
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
+
j
∗
f
[
i
−
1
]
[
j
]
f[i][j] = f[i-1][j-1] +j*f[i-1][j]
f[i][j]=f[i−1][j−1]+j∗f[i−1][j]
第
i
个
数
单
独
分
成
一
组
是
f
[
i
−
1
]
[
j
−
1
]
第i个数单独分成一组是f[i-1][j-1]
第i个数单独分成一组是f[i−1][j−1]
或
者
第
i
个
数
分
成
前
面
已
经
有
的
j
组
中
的
其
中
一
组
j
∗
f
[
i
−
1
]
[
j
]
或者第i个数分成前面已经有的j组中的其中一组j * f[i-1][j]
或者第i个数分成前面已经有的j组中的其中一组j∗f[i−1][j]
那
么
考
虑
一
下
加
上
题
目
给
的
限
制
条
件
那么考虑一下加上题目给的限制条件
那么考虑一下加上题目给的限制条件
每
一
组
的
数
%
m
的
值
没
有
相
等
的
每一组的数\%m的值没有相等的
每一组的数%m的值没有相等的
也
就
是
说
第
i
个
数
分
成
前
面
已
经
有
的
j
组
中
的
其
中
一
组
也就是说第i个数分成前面已经有的j组中的其中一组
也就是说第i个数分成前面已经有的j组中的其中一组
必
须
保
证
分
到
的
那
个
组
中
没
有
当
前
这
个
数
%
m
的
值
出
现
必须保证分到的那个组中没有当前这个数\%m的值出现
必须保证分到的那个组中没有当前这个数%m的值出现
所
以
先
维
护
一
个
c
n
t
数
组
所以先维护一个cnt数组
所以先维护一个cnt数组
表
示
a
[
i
]
%
m
的
个
数
表示a[i] \% m的个数
表示a[i]%m的个数
在
放
第
i
个
数
的
时
候
如
果
已
经
放
过
了
这
个
数
了
在放第i个数的时候如果已经放过了这个数了
在放第i个数的时候如果已经放过了这个数了
放
下
一
个
数
的
时
候
就
不
能
在
放
这
个
数
放下一个数的时候就不能在放这个数
放下一个数的时候就不能在放这个数
所
以
状
态
方
程
就
变
成
了
所以状态方程就变成了
所以状态方程就变成了
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
+
k
∗
f
[
i
−
1
]
[
j
]
f[i][j] = f[i-1][j-1] +k*f[i-1][j]
f[i][j]=f[i−1][j−1]+k∗f[i−1][j]
但
是
这
样
还
是
有
一
个
问
题
但是这样还是有一个问题
但是这样还是有一个问题
第
一
层
循
环
枚
举
的
是
i
也
就
是
这
个
数
第一层循环枚举的是i也就是这个数
第一层循环枚举的是i也就是这个数
第
二
层
循
环
枚
举
的
是
j
组
数
第二层循环枚举的是j组数
第二层循环枚举的是j组数
我
们
发
现
不
能
不
重
不
漏
的
进
行
转
移
我们发现不能不重不漏的进行转移
我们发现不能不重不漏的进行转移
因
为
必
须
保
证
每
个
数
是
按
照
顺
序
放
的
因为必须保证每个数是按照顺序放的
因为必须保证每个数是按照顺序放的
所
以
先
枚
举
组
数
j
所以先枚举组数j
所以先枚举组数j
在
枚
举
i
在枚举i
在枚举i
在
组
数
固
定
情
况
下
在组数固定情况下
在组数固定情况下
依
次
放
入
每
个
数
即
可
依次放入每个数即可
依次放入每个数即可
k
的
求
法
具
体
细
节
可
以
看
代
码
k的求法具体细节可以看代码
k的求法具体细节可以看代码
时间复杂度:
O
n
2
On^2
On2
#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int N = 1e6 + 10 , M = 5010 , inf = 0x3f3f3f3f , mod = 998244353 ;
int f[M][M] ;
// 前i个数分成j组的方案数
int n , m ;
int a[N] ;
// 1到i的每个数
int cnt[M] ;
// 每个数 % m的个数
int q[M] ;
// 工具数组
signed main()
{
cin >> n >> m ;
fer(i,1,n)
{
a[i] = i ;
cnt[i % m] ++ ;
}
f[0][0] = 1 ; // 初始化
for(int j = 1 ; j <= n ; j ++)
{
// 每次固定j组数
memcpy(q,cnt,sizeof cnt) ;
// 把cnt数组给q数组
for(int i = 1 ; i <= n ; i ++)
{
f[i][j] = (f[i-1][j-1] + f[i-1][j] * (j - ( cnt[a[i] % m] - q[a[i] % m] ) )) % mod;
// k = j - ( cnt[a[i] % m] - q[a[i] % m] )
// 表示用j所有组减去前面有多少个组放了这个数 % m
q[a[i] % m] -- ;
// 放了这个数之后 这个数 % m --
// 表示j之前的所有组中放了a[i] % m 的组 ++
}
}
for(int i = 1 ; i <= n ; i ++) cout << f[n][i] % mod << "\n" ;
return 0;
}