这一场真的不难,前四题都是思维题,感觉比上一场Div.4要简单的多
平时没怎么做过交互题,所以花费了太长时间去理解,后面的题补了再写题解
A.Adjacent Digit Sums
题意:给两个数 x , y x,y x,y,看是否有一个数 n n n满足 n n n和 n + 1 n+1 n+1的数位之和分别等于 x x x和 y y y
一共分两种情况,对于一个数,如果末位不为9的话,给这个数+1那么数位之和一定会增加1,如果为9的话,那么数位之和会减少 9 ∗ i − 1 9*i-1 9∗i−1, i i i表示这个数末尾9的个数,根据 x x x和 y y y的关系判断即可
时间复杂度 O ( 1 ) O(1) O(1)
void solve(){
int x,y;cin>>x>>y;
if(y>x){
if(y-x == 1){
cout<<"Yes"<<endl;
}
else{
cout<<"No"<<endl;
}
}
else{
if((x-y+1)%9 == 0){
cout<<"Yes"<<endl;
}
else{
cout<<"No"<<endl;
}
}
}
B. Two Large Bags
题意:给出一个有 n n n个数字的集合 A A A,和一个空集合 B B B,可以选择将 A A A中的一个数放到 B B B中或如果两个集合有相同的数,那么将 A A A中的这个数 + 1 +1 +1,求任意次操作后,是否可以将两个集合变成一样的。
这个题我的代码写的很啰嗦,而且思路也比较麻烦,可能中间有一些不需要的操作,或者已经被其他操作包含了,想清楚这个过程就行。
首先可以想到如果有一个数 x x x的个数为 2 2 2,并且集合中存在 x + 1 x+1 x+1,则可以将一个数一直加上去,相当于将后续连续的一些数当作一条路,可以一直加上去,但 x x x的个数会少 1 1 1,注意我们每个数都至少需要 2 2 2个且个数不能为奇数,那么如果集合中 x x x多于 3 3 3个,那么我们可以存起来,直到有一个数的个数为 0 0 0或者为 1 1 1,我们可以用之前存起来的数去补充,直到遇到某个数的个数小于 2 2 2并且存起来的数不够将其补充到 2 2 2,那么如果之前存起来的数还剩 1 1 1个,那么是不可能达到最终要求的,然后再去看后面新的连续的数。用哈希表处理,遍历这个哈希表,将每个数检查一遍即可。
时间复杂度 O ( n ) O(n) O(n)
void solve(){
int n;cin>>n;
vector<int>a(n);
vector<int> has(10005);
int ma = 0;
for(int i = 0;i<n;i++){
cin>>a[i];
ma = max(ma,a[i]);
has[a[i]]++;
}
int jud = 0;
int ans = 0;
for(int i = 1;i<=ma;i++){
if(has[i] == 0){
if(ans == 1){
cout<<"No"<<endl;
return;
}
else if(ans>=2){
ans-=2;
has[i] = 2;
}
continue;
}
if(has[i] == 2){
continue;
}
else if(has[i] == 1){
if(ans == 0){
cout<<"No"<<endl;
return;
}
else{
ans--;
}
}
else if(has[i]>2){
ans+=has[i]-2;
}
}
if(ans%2 == 1){
cout<<"No"<<endl;
}
else cout<<"Yes"<<endl;
}
C. Devyatkino
题意:给一个正整数 n n n,可以将其加 9 , 99 , 999 , 9999... , 9,99,999,9999..., 9,99,999,9999...,只包含9的数字,求加到数字中包含 7 7 7至少需要几次
没什么想法的话先打个表,打完表之后就可以发现,如果加9的话,每加一次个位会 − 1 -1 −1,十位会 + 1 +1 +1,后面的百位也会增加(个位为0的情况除外,比如90+9 = 99),加99,999的情况同上,最开始我还在算每一位需要加多少次。其实没那么麻烦,因为每加10次就是一个循环,所以直接加 1 0 i − 1 ( 1 < = i < = 10 ) 10^i-1(1<=i<=10) 10i−1(1<=i<=10),每个数加10次即可。
我的做法是纯暴力去算,肯定有更快的做法,但是做这个题足够了(其实我打完表之后发现每一个位的变化规律,但是不知道怎么证明为什么不是+99后再+9这种混着加的情况)。
void solve(){
i64 s;cin>>s;
int ans = 1e9;
for(int i = 1;i<=10;i++){
i64 cnt = 1,tmp = s;
for(int j = 1;j<=i;j++){
cnt*=10;
}
cnt--;
for(int j = 1;j<=100;j++){
i64 b = tmp;
tmp+=cnt;
int f = 0;
while(b){
if(b%10 == 7){
ans = min(ans,j-1);
f = 1;
break;
}
b/=10;
}
if(f == 1){
break;
}
}
}
cout<<ans<<endl;
}
D. Object Identification
这个题我觉得挺有意思的,首先想到有向图中最短路和坐标中曼哈顿距离最大的区别是,曼哈顿距离没有方向,而图中的边是有方向的,而且图中点的距离范围是比较苛刻的。
如果给出的 x x x不是一个排列,那么至少有一个点的出度是 0 0 0,也就是说以这个点为起点,不管到哪一条边的距离都没有路,题目中这种情况会输出0,而因为没有重合的点,所以在坐标系中不会出现距离为0的情况,直接输出 x x x中不存在的点和其他任意一个点,如果为 0 0 0就是 A A A,如果为 1 1 1就是 B B B。
如果是一个排列,注意这个图中点的数量和边的数量相等,这也就限制了任意两点之间的距离 < = n − 1 <=n-1 <=n−1,而坐标系中点1到点n的距离一定 > = n − 1 >=n-1 >=n−1,我们选择1和n两个点,如果 < n − 1 <n-1 <n−1就输出A,如果 > n − 1 >n-1 >n−1就输出B,那么等于 n − 1 n-1 n−1的情况呢,这时我们进行第二次询问,因为图中 点的数量$ = $边的数量,所以如果存在从 1 1 1到 n n n的路径,那么从 n n n到 1 1 1的路径这两条路径是不相等的并且有一个为 1 1 1,而坐标系中这两个点的距离是相等的,根据这点可以判断出是哪个图。
void solve(){
int n;
cin >> n;
vector<int> x(n);
vector<bool> present(n + 1, false);
for (int i = 0; i < n; ++i) {
cin >> x[i];
present[x[i]] = true;
}
int u = -1;
for (int i = 1; i <= n; ++i) {
if (!present[i]) {
u = i;
break;
}
}
if (u != -1) {
int v = (u == 1) ? 2 : 1;
cout << "? " << u << " " << v << endl;
fflush(stdout);
int res;
cin >> res;
if (res == 0) {
cout << "! A" << endl;
} else {
cout << "! B" << endl;
}
} else {
int u,v;
for(int i = 0;i<n;i++){
if(x[i] == 1){
u = i+1;
}
if(x[i] == n){
v = i+1;
}
}
cout << "? " << u << " " << v << endl;
fflush(stdout);
int d1;
cin >> d1;
if(d1<n-1){
cout<<"! A"<<endl;
fflush(stdout);
}
else if(d1 > n-1){
cout<<"! B"<<endl;
fflush(stdout);
}
else{
cout << "? " << v << " " << u << endl;
fflush(stdout);
int d2;
cin >> d2;
if (d1 != d2) {
cout << "! A" << endl;
} else {
cout << "! B" << endl;
}
}
}
}