A
暴力即可
#include <iostream>
#include <algorithm>
#include <unordered_map>
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int N = 1e3+10;
int a[N],n,t;
int _abs(int x){
return x>=0?x:-x;
}
void change(int i){
if(i==0) return;
if(i&1){
for(int i=1;i<=n-2;i+=2){
if(a[i]>a[i+1]) swap(a[i],a[i+1]);
}
}else{
for(int i=2;i<=n-1;i+=2){
if(a[i]>a[i+1]) swap(a[i],a[i+1]);
}
}
}
bool jg(){
for(int i=1;i<=n;++i){
if(a[i] != i) return false;
}
return true;
}
int main()
{
SIS;
cin >>t;
while(t--){
cin >> n;
for(int i=1;i<=n;++i) cin >> a[i];
int start = 0;
bool flag;
while(1){
change(start);
flag = jg();
if(flag){
cout << start << '\n';
break;
}
++start;
}
}
return 0;
}
B
分奇数和偶数讨论,可以发现奇数比偶数多一种情况;
对于选手来说,如果花费了一个点数去打break
,那么他自己也必定会被break
一次;
因此 一 个 点 数 = 两 次 b r e a k 一个点数=两次break 一个点数=两次break
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const int N = 1e3+10;
ll a,b,t;
set<ll> s;
ll _abs(ll x){
return x>=0?x:-x;
}
void solve(){
if(min(a,b) == 0){
ll mx = max(a,b);
if(mx&1){
cout << 2 << '\n';
cout << (mx/2) << ' ' << (mx/2+1) << '\n';
}else{
cout << 1 << '\n';
cout << (mx/2) << '\n';
}
return;
}
ll sum = a+b;
ll minn = min(a,b);
//奇数和偶数都有sum/2个 minn对应字母
ll tmp = sum/2 - minn;
if(minn <= sum/2){
s.insert(tmp);
}
ll tmin = minn;
while(tmin--){
s.insert(tmp+2);
tmp+=2;
}
if(sum&1){
//奇数额外多了这种情况
//有sum/2+1个minn对应字母
tmp = sum/2+1 - minn;
if(minn <= sum/2+1){
s.insert(tmp);
}
tmin = minn;
while(tmin--){
s.insert(tmp+2);
tmp+=2;
}
}
cout << s.size() << '\n';
for(auto z : s){
cout << z << ' ';
}
cout << '\n';
}
int main()
{
SIS;
cin >> t;
while(t--){
s.clear();
cin >> a >> b;
solve();
}
return 0;
}
C
计算出打通每个洞穴所需要的最少体力;
二分答案即可
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const int N = 1e5+10;
ll a,b,t,n;
vector<ll> v[N];
struct Node{
ll v,idx;
}mx[N];
bool cmp(Node p,Node q){
return p.v < q.v;
}
bool jg(ll hp){
for(int i=1;i<=n;++i){
if(hp>=mx[i].v){
hp+=v[mx[i].idx].size();
}else{
return false;
}
}
return true;
}
int main()
{
SIS;
cin >> t;
while(t--){
cin >> n;
for(ll i=0;i<=n+1;++i){
mx[i].v = mx[i].idx = 0;
v[i].clear();
}
for(ll i=1,k;i<=n;++i){
cin >> k;
mx[i].idx = i;
for(ll j=1,u;j<=k;++j){
cin >> u;
v[i].push_back(u);
ll len = v[i].size()-1;
mx[i].v = max(mx[i].v,u-len+1);
}
}
sort(mx+1,mx+1+n,cmp);
ll L = -1,R = mx[n].v;//(L,R]
while(L+1<R){
ll mid = (L+R) >> 1;
if(jg(mid)) R = mid;
else L = mid;
}
cout << R << '\n';
}
return 0;
}
D1
首先根据题目的意思,打出暴力的代码;
这份代码的 f [ i ] 表 示 从 n 到 i 的 方 案 数 f[i]表示从n到i的方案数 f[i]表示从n到i的方案数
#include <iostream>
#include <unordered_map>
using namespace std;
const int N = 2e5+10;
typedef long long ll;
int n,m;
int f[N];
int main(){
cin >> n >> m;
f[n] = 1;
for(int i=n;i>=2;--i){
for(int j=1;j<=i-1;++j){
f[i-j] = (f[i-j]+f[i]) % m;
}
for(int j=2;j<=i;++j){
f[i/j] = (f[i/j]+f[i]) % m;
}
}
cout << f[1] << '\n';
return 0;
}
这份代码会TLE,那我们考虑如何优化它;
因为是计数,那么我从n记录到1,和从1反向记录到n,他们的结果是相同的;
那么我们先改变一下代码,如下;
这份代码的 f [ i ] 表 示 从 1 到 i 的 方 案 数 f[i]表示从1到i的方案数 f[i]表示从1到i的方案数
#include <iostream>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n,m;
int f[N];
int main(){
cin >> n >> m;
f[1] = 1;
for(int i=2;i<=n;++i){
for(int j=1;j<=i-1;++j){
f[i] = (f[i-j]+f[i]) % m;
}
for(int j=2;j<=i;++j){
f[i] = (f[i/j]+f[i]) % m;
}
}
cout << f[n] << '\n';
return 0;
}
我们可以发现,对于每个i来说,我们都要计算 1 到 i − 1 的 和 1到i-1的和 1到i−1的和,那么我们可以用前缀和来维护,这样就优化掉一个循环了,如下;
#include <iostream>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n,m;
int f[N];
int main(){
cin >> n >> m;
f[1] = 1;
int sum = 1;
for(int i=2;i<=n;++i){
f[i] = (sum%m+f[i]%m) % m;
for(int j=2;j<=i;++j){
f[i] = (f[i/j]+f[i]) % m;
}
sum=(sum%m+f[i]%m)%m;
}
cout << f[n] << '\n';
return 0;
}
然而还是会T,那么我们要继续优化;
这里需要引入一个知识整除分块
,具体可以参考这篇博客整除分块;
这里我写一下我的思路;
首先打表看一下
可以发现,表中同样的值会连续出现,成块状分布,所以从
1
到
n
1到n
1到n的数组表可根据数值划分为不同的分块,且分块数远远小于 n;
如果我们是枚举统计每个值,那么是 O ( n ) O(n) O(n)的,而按照分块来统计,则是 O ( n ) O(\sqrt{n}) O(n);
我们想一次统计一个块的话,那么就需要知道这个块的左右坐标,记为 L 和 R L和R L和R;
令
k
=
⌊
n
i
⌋
令k=\lfloor \frac{n}{i} \rfloor
令k=⌊in⌋;
最小的
i
就
是
L
,
最
大
的
i
就
是
R
i就是L,最大的i就是R
i就是L,最大的i就是R
而
L
L
L我们只需要枚举(见代码就清晰了),R需要计算;
显然有 k ∗ i ≤ n k*i≤n k∗i≤n,我们想让 i 尽 可 能 的 大 , 最 好 能 满 足 k ∗ i = n i尽可能的大,最好能满足k*i=n i尽可能的大,最好能满足k∗i=n;
但是往往取不到,因此我们只需要让 i = ⌊ n k ⌋ i=\lfloor \frac{n}{k} \rfloor i=⌊kn⌋即可取到最大的 i i i;
将 k = ⌊ n i ⌋ k=\lfloor \frac{n}{i} \rfloor k=⌊in⌋代入 i m a x = ⌊ n k ⌋ i_{max}=\lfloor \frac{n}{k} \rfloor imax=⌊kn⌋,可以得到 i m a x = ⌊ n ⌊ n i ⌋ ⌋ i_{max}=\lfloor \frac{n}{\lfloor \frac{n}{i} \rfloor} \rfloor imax=⌊⌊in⌋n⌋
这样我们就可以将
for(int j=2;j<=i;++j){
f[i] = (f[i/j]+f[i]) % m;
}
优化成
for(int L=2,R;L<=i;L=R+1){
R = i/(i/L);
f[i] = (f[i]+(R-L+1)*f[i/L])%m;
}
完整Code
#include <iostream>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n,m;
ll f[N];
int main(){
cin >> n >> m;
f[1] = 1;
int sum = 1;
for(int i=2;i<=n;++i){
f[i] = sum%m;
for(int L=2,R;L<=i;L=R+1){
R = i/(i/L);
f[i] = (f[i]+(R-L+1)*f[i/L])%m;
}
sum=(sum%m+f[i]%m)%m;
}
cout << f[n] << '\n';
return 0;
}