A. Recovering a Small String
大意为,寻找三个字母(编号为1~26)使其之和为x,要求字典序最小:
可以初始化全为a,之后从后往前对每一位判断x是不是大于等于25,是的话,直接将该位变成z,否则变成 a + x,x小于零后直接退出循环就可,代码如下:
#include <iostream>
using namespace std;
int main()
{
int t;
cin >> t;
while(t--)
{
int x;
cin >> x;
char a[3] = {'a', 'a', 'a'};
x-=3;
for(int i = 2; i >= 0; i--)
{
if(x >= 25)
{
a[i] += 25;
}
else
{
a[i] += x;
}
x-=25;
if(x < 0) break;
}
for(int i = 0; i < 3; i++) cout << a[i];
cout <<endl;
}
return 0;
}
B.Make Equal
大意为,给定一组n个数据,这组数据总和能够被n整除,可以选择ai,将其减去x,将x加在aj上,并且 i < j,能不能满足最后的数全部相等。
假设最后会全部相等,那么这个相等的数就是这n个数的平均值,因此我们可以把平均值先求出来,定一个初始值为0的ans,再从前往后判断这组数据中的数是否大于或小于平均值,大于的话让ans+(ai - 平均值),小于的话,让ans - (平均值 - ai),中间如果ans < 0,那就代表不可以,加个flag判断就可以了,代码如下:
#include <iostream>
#include <vector>
using namespace std;
#define int long long
const int N = 2e5+10;
signed main()
{
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
vector<int> v(n+1);
int x;
int sum = 0;
for(int i = 0; i < n; i++)
{
scanf("%d",&x);
sum += x;
v[i] = x;
}
int ave = sum / n;
int ans = 0;
bool flag = true;
for(int i = 0; i < n; i++)
{
ans += v[i] - ave; //ans - (ave - v[i]) = ans + (v[i] - ave)
if(st < 0)
{
flag = false;
break;
}
}
if(flag) puts("Yes");
else puts("No");
v.clear();
}
return 0;
}
C. Make Equal Again
题目大意为,给定一组数据,选择下标从 i 到 j 的所有的数让其变为x,问能将所有的数变的相同的长度( j - i + 1 )最小为多少.
判断一下左边有多少连续相同的(记为l),右边有多少相同的(记为r),再判断第一个和最后一个是否相同,相同的话,就减去两边的值,不同的话,比较减去两边的之后哪个小选哪个,(下边我的代码中的 l 和 r 记录的是与前边或后边不同的元素的下标),代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2e5+10;
int a[N];
int main()
{
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
int l = 2, r = n; //让l从2开始,与左边不同的数的下标至少为2(n足够大时)
for(int i = 1; i <= n; i++)
{
scanf("%d",&a[i]);
}
for(int i = 2; i <= n; i++)
{
if(a[i] == a[i - 1])
{
l = i + 1;
}
else break;
}
for(int i = n - 1; i > 0; i--)
{
if(a[i] != a[i + 1])
{
r = i;
break;
}
}
int ans = 0;
//判断第一个数如果与最后一个数不相等,那么就只需要比较min(n - l + 1, r),否则让ans为总长度减去两边相同的数的长度
if(a[1] != a[n])
{
ans = min(n - l + 1, r);
}
else
{
ans = n - l + 1 - (n - r );
}
cout << ans <<endl;
memset(a, 0, sizeof a);
}
return 0;
}
D. Divisible Pairs
题目大意为寻找ai + aj(i < j),能被x整除,ai - aj能被y整除的<i, j>的数对的数量。
由(ai + aj)%x=0得,ai%x + aj%x = 0 ---> ai %x = x - aj%x,(ai - aj)%y = 0 ---> ai % y = aj % y,因此我们可以将每个数分别模上x、y,并将它存在一个pair中,利用map映射,再后边中的数中找到能与他对应的数,代码如下:
#include <iostream>
#include <map>
#include <vector>
using namespace std;
#define int long long
using PII = pair <int,int>;
void solve()
{
int n, x, y;
cin >> n >> x >> y;
vector<int> a(n+2), b(n+2);
int q;
for(int i = 1; i <= n; i++)
{
cin >> q;
//(ai+aj) % x == 0 --> aj % x + ai % x ==0 --> aj % x = (x - ai) % x;
//(ai - aj) % y == 0 --> ai % y == aj % y;
a[i] = q % x;
b[i] = q % y;
}
map<PII,int> mp;
int ans = 0;
int m1, m2;
for(int i = 1; i <= n; i++)
{
m1 = (x-a[i])%x;
m2 = b[i];
ans += mp[{m1,m2}];
mp[{a[i], b[i]}] ++ ; //会统计在后边出现与之互补的为这个数的数量
}
cout<<ans<<endl;
}
signed main()
{
int t;
cin >> t;
while(t--)
{
solve();
}
return 0;
}
E. Anna and the Valentine's Day Gift
题目大意为,Anna可以将某个数反转,并让后边的零消失,比如2100 --> 12,Sasha可以让某两个数以任意顺序合并在一起,如果最后还剩的这个数大于10的m次方,输出Sasha,否则输出Anna。
按照题意理解,Anna可以减少零的个数,Sasha可以保护零不减少,记录下来总的数据的位数个数,和每个数据零的个数进行模拟即可,代码如下:
//Anna会把零减少,Sasha会保护零不减少
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void solve()
{
int n, m;
cin >> n >> m;
vector<int> v;
int x1, x2;
int cnt;
int s = 0;//记录总的数的位数
for(int i = 0; i < n; i++)
{
cin >> x1;
x2 = x1;//复制一份x1
cnt = 0;//记录一个数中0的个数
while(x1%10==0)
{
cnt ++;
x1/=10;
}
if(cnt) v.push_back(cnt);
while(x2)
{
s++;
x2/=10;
}
}
sort(v.begin(), v.end());
int c = 1;//决定轮到谁
while(v.size())
{
int t = v.back();
v.pop_back();
if(c & 1) s -= t;
c ^= 1;
}
if(s > m) puts("Sasha");
else puts("Anna");
}
int main()
{
int t;
cin >> t;
while(t--)
{
solve();
}
return 0;
}
F. Chat Screenshots
题目大意为每个人写出自己看到每个人的顺序,且在自己看来,他的位置是第一位,其他人的相对位置是正确的,求对于这n个人是否拥有一个确切的位置顺序,有的话,输出yes,否则输出,no。
可以看为一个拓扑排序,统计从第二个人开始的每个人的位置关系,判断有无环,有环无解,代码如下:
//拓朴排序,无环代表有解
#include <iostream>
#include <queue>
using namespace std;
const int N = 2e5+10;
int e[N], ne[N], h[N], idx = 1;
int du[N];
int a[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void solve()
{
int n, k;
cin >> n >> k;
for(int i = 0; i < k; i++)
{
for(int j = 0; j < n; j++)
{
cin >> a[j];
if(j >= 2)
{
add(a[j - 1], a[j]);
du[a[j]] ++;
}
}
}
queue<int> q;
for(int i = 1; i <= n; i++)
if(!du[i]) q.push(i);
while(q.size())
{
int t = q.front();
q.pop();
for(int i = h[t]; i ; i = ne[i])
{
if(--du[e[i]] == 0) q.push(e[i]);
}
}
bool flag = true;
for(int i = 1; i <= n; i++)
{
if(du[i])
{
flag = false;
break;
}
}
if(flag) puts("Yes");
else puts("No");
for(int i = 1; i <= n; i++) h[i] = du[i] = 0;
idx = 1;
}
int main()
{
int t;
cin >> t;
while(t--)
{
solve();
}
return 0;
}
G. One-Dimensional Puzzle
题目大意为有四种拼图,现给出每种拼图的数量,询问能排成一列的拼图的种类有多少
前置知识,将a个相同的物品放在b个盒子中(可以不放),方案数为C ,(有点不好看先将就着吧),快速幂,组合数,逆元的求法
观察可得一二块拼图可以交替排列,但三四只能插入其中,或者不插入其中排在它们的后边,还需要在意一点,第一二个拼图相差数量不能超过1,进行分类讨论就好,代码如下:
//将x个相同物品放在y个箱子中(允许不放),方案数为 (y - 1)! / (x + y - 1)!/(x+y-1-(y-1))!
#include <iostream>
using namespace std;
using ll = long long;
const int N = 2e6+10, mod = 998244353;
ll fact[N], infact[N];
int qmi(ll a, ll k, ll p)
{
int res = 1;
while(k)
{
if(k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
void init()
{
fact[0] = infact[0] = 1;
for(int i = 1; i < N; i++)
{
fact[i] = fact[i-1] * i % mod;
infact[i] = infact[i-1] * qmi(i, mod - 2, mod) % mod;
}
}
ll C(int a, int b)
{
return fact[a] * infact[a-b]%mod * infact[b] %mod;
}
void solve()
{
int a, b, c, d;
scanf("%d%d%d%d",&a,&b,&c,&d);
ll ans = 0;
if(abs(a - b) > 1 )
{
ans = 0;
}
else if(a == 0 && b == 0)
{
if(c == 0 || d == 0)
{
ans = 1;
}
}
else if(a == b)
{
ans = (C(c+a-1,a-1)*C(d+a+1-1, a+1-1)%mod + C(c+a+1-1, a+1-1) * C(d+a-1, a-1)%mod) % mod;
}
else if(a == b-1)
{
ans = C(c+a + 1-1, a+1-1)*C(d+b-1, b-1)%mod;
}
else
{
ans = C(c+ a-1, a-1) * C(d+b+1-1, b+1-1) % mod;
}
printf("%lld\n",ans);
}
int main()
{
init();
int t;
scanf("%d",&t);
while(t--)
{
solve();
}
return 0;
}