A.Array Recovery
题意
给定一个长度为n的数组d,请你构造一个唯一的非负整数数组a,满足:
d1 = a1,di=|ai - a(i - 1)|;
思路
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 109;
int a[N], d[N];
void solve()
{
int n;cin >> n;
for(int i = 1;i <= n; ++ i)cin >> d[i];
a[1] = d[1];
for(int i = 2;i <= n; ++ i)
{
if(d[i] && a[i - 1] - d[i] >= 0)
{
cout << -1 << '\n';
return;
}
a[i] = a[i - 1] + d[i];
}
for(int i = 1;i <= n; ++ i)cout << a[i] << ' ';
cout << '\n';
}
int main()
{
int _;cin >> _;
while(_ --)solve();
return 0;
}
B.Reverse Sort
题意
给定一个长度为n的0101串,每次操作可以选择一段“非递增”的子序列,并将其翻转,问是否存在一种方案使整个0101串变为升序(类似0000111100001111的串)。
思路
先把特殊情况特判,就是0101串一开始就已经是递增的了,那么无需操作,操作次数为0。
对于这种可能有若干次操作的题目,我们可以假定它“一定可以在一次(或两次)操作中完成”,于是问题就变为了如何构造出一种仅需一次的操作方法。
题目要求操作后0101串变为升序,于是我们可以知道:
1.子序列里面肯定得有11也有00,否则翻转没有意义。
2.我们要做的是将右侧的00搬到左侧,左侧的11搬到右侧。
于是我们可以构造出一种操作方法,使得对于非特殊情况,一次操作一定可以使得串变为升序。
左右跑双指针即可,对于每一个左侧的11都有一个右侧的00与之对应即可。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e6 + 9;
char s[N];
void solve()
{
int n;cin >> n;
cin >> s + 1;
bool tag = true;//tag表示01串为升序
for(int i = 2;i <= n; ++ i)if(s[i] < s[i - 1])tag = false;
if(tag)cout << 0 << '\n';
else
{
vector<int> v;
int l = 1, r = n;
while(l < r)
{
while(l < r && s[l] == '0')l ++;
while(l < r && s[r] == '1')r --;
if(l < r)//注意一定要保证l < r才能加到答案里
{
v.push_back(l), v.push_back(r);
l ++, r --;
}
}
//输出前记得排序
sort(v.begin(), v.end());
cout << 1 << '\n' << v.size() << ' ';
for(const auto &i : v)cout << i << ' ';
cout << '\n';
}
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int _;cin >> _;
while(_ --)solve();
return 0;
}
C.Make It Round
题意
给定两个整数n,m,你可以对n乘上一个整数k(1≤≤)k(1≤k≤m),问选哪个k,可以使得n×k的结果的尾部的00最多。
如果多种方案尾部00的数量相等,输出结果较大的。
思路
因为这题的m范围较大,且样例数比较多,所以逐个枚举k显然是会超时的。
于是我们去分析一下"尾部0的个数"与哪些变量有关,这个思想实际上也是我第一场的A中所说过的,将一个抽象的东西,转化为具体的可量化的变量来分析。
每多一个00,说明n×k中就多一个因子1010,也就是说n×k的质因数中就多一对$(2, 5)。
再稍微分析一下,我们设f(x,i)表示x中质因子i的个数,那么:
cnt0=min(f(nk,2),f(nk,5))
并且有一个数学规律:
f(xy,i)=f(x,i)+f(y,i)
n是一个确定的数字,所以f(n,2)和f(n,5)是确定的,可以先计算出来,我们要做的,就是构造一个数字k,它只有22和55两种质因子(因为其他质因子是没有价值的,还会让数字变大),并且保证k≤m,先让f(nk,2)和f(nk,5)相等,然后又再对k持续乘10即可。
现在还有一个问题,就是题目要求这个nk尽可能大,我们做完前面的操作后1≤k≤m,这里可能还存在一些变大的空间,于是我们可以计算出来,假设k还可以扩大p倍,于是有:
k×p≤m
p≤⌊km⌋
最后给答案乘上一个⌊k/m⌋即可,在这里扩大p倍是一定不会改变尾部0的个数的。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve()
{
ll n, m;cin >> n >> m;
ll k = 1, t = n;//t用于计算c2和c5
int c2 = 0, c5 = 0;
while(t % 2 == 0)t /= 2, c2 ++;
while(t % 5 == 0)t /= 5, c5 ++;
while(k * 2 <= m && c2 < c5)k *= 2, c2 ++;
while(k * 5 <= m && c5 < c2)k *= 5, c5 ++;
while(k * 10 <= m)k *= 10;
cout << n * k * (m / k) << '\n';
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int _;cin >> _;
while(_ --)solve();
return 0;
}
D.Great Sequence
题意
给定两个整数n,x和一个长度为n数组a,问你至少新增多少个数字,可以使得数组中的数字可以两两配对,并使得每一个二元组(t1,t2)满足:t1×x=t2。
思路
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve()
{
ll n, x;cin >> n >> x;
multiset<ll> st;
for(int i = 1;i <= n; ++ i)
{
ll y;cin >> y;
st.insert(y);
}
int ans = 0;
while(st.size())
{
ll y = *st.begin();
if(st.find(y * x) == st.end())//如果x * y不存在
{
st.erase(st.begin());
ans ++;
}else
{
//注意这里不能写st.erase(x * y),这会把所有x * y都删除
//但是我们只想删除一个
st.erase(st.find(x * y));
st.erase(st.begin());
}
}
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int _;cin >> _;
while(_ --)solve();
return 0;
}
E. Playing in a Casino (数学贡献法)
核心思想
贡献法的核心思想就是,将原本难以计算的整体拆分为较小的部分,并从小部分出发,去计算每一个部分被计算过多少次,从而加速计算。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e6 + 9;
vector<vector<ll> > v;
void solve()
{
int n, m;cin >> n >> m;
//v有m列
v.clear();
v.resize(m + 1);
for(int i = 1;i <= n; ++ i)
{
for(int j = 1;j <= m; ++ j)
{
int x;cin >> x;
v[j].push_back(x);
}
}
ll ans = 0;
for(int i = 1;i <= m; ++ i)
{
//引用类型t = v[i]
vector<ll>& t = v[i];
sort(t.begin(), t.end());
for(int j = 1;j < n; ++ j)
{
//j是左端点的个数,n - j为右端点的个数
ans += 1ll * j * (n - j) * (t[j] - t[j - 1]);
}
}
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int _;cin >> _;
while(_ --)solve();
return 0;
}