Harbour.Space Scholarship Contest 2021-2022 (open for everyone, rated, Div. 1 + Div. 2) 题解A-E

A. Digits Sum

分析:显然以9结尾的十进制数都符合题目要求

#include <iostream>
#include <algorithm>
using namespace std;

int main()
{
    int t; cin >> t;
    while(t--)
    {
        int n; cin >> n;
        cout << (n/10) + ((n%10)/9) << endl;
    }
    // system("pause");
}

B. Reverse String

分析:
枚举起点,转折点,然后朴素匹配
时间复杂度是 O ( n 3 ) O(n^3) O(n3)

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
char s[1000], t[1000];
int main()
{
    int T; cin >> T;
    while(T--)
    {
        scanf("%s %s",s,t);
        int n = strlen(s);
        int m = strlen(t);
        bool flag = 0;
        for(int i = 0; i < n; i++)
        {
            for(int j = i; j < n; j++)
            {
                int u = i;
                bool f = 0;
                for(int k = 0; k < m;)
                {
                    if(u == j+1) u = j-1, f = 1;
                    if(u < 0 || u >= n) break;
                    if(s[u] == t[k]) f? u--: u++, k++;
                    else 
                    {
                        // printf("! %d %c %d %c\n", u,s[u], k,t[k]);
                        break;
                    }
                    if(k == m) flag = 1;
                }
            }
        }
        if(flag) printf("YES\n");
        else printf("NO\n");
    }
    // system("pause");
}

C. Penalty

分析:
维护第一组最多可能的胜场数,最少的可能胜场数,第二组最多可能的胜场数,最少可能的胜场数。
一边递推一边判断是不是可以结束比赛即可。
时间复杂度 O ( n 2 ) O(n^2) O(n2)

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
char s[20];
int mx[2], mn[2];
int main()
{
    int t; cin >> t;
    while(t--)
    {
        scanf("%s",s+1);
        int ans = 10;
        mn[0] = mn[1] = mx[0] = mx[1] = 0; 
        for(int i = 1; i <= 10; i++)
        {
            if(s[i] == '1') mx[i&1]++, mn[i&1]++;
            else if(s[i] == '?') mx[i&1]++;
            if((mx[i&1]-mn[(i&1)^1] > (10-i+(i&1))/2) || mx[(i&1)^1] - mn[i&1] > (10-i)/2 )
            {
                ans = ans>i? i : ans;
                // printf("%d %d %d %d\n",ans,mx[i&1],mn[(i&1)^1] , (10-i+(i&1))/2);
            }

        }
        cout << ans << endl;
    }
    // system("pause");
}

D. Backspace

分析:
考虑题目所给的操作可以做到在原串上从开头删除任意多个连续的字符,从中间删除偶数多的字符。
于是考虑从后往前匹配,一旦匹配完毕,剩余的原串即可通过给定操作全部删除。
再考虑到以下的性质:
记目前原串 s s s匹配到的位置是 i i i, 结果串 t t t匹配到的位置是 j j j
如果 s [ i ] = t [ j ] s[i] = t[j] s[i]=t[j],此时有两种情况,保留 i i i或者删除 s [ i − 1 , i ] s[i-1,i] s[i1,i]两个字符

  1. 如果保留,那么 t [ j − 1 ] t[j-1] t[j1]可以匹配的位置是 s [ i − 1 − 2 k ] , k ∈ { 0 , 1 , 2 , . . . . } s[i-1-2k], k\in\{0,1,2,....\} s[i12k],k{0,1,2,....}
  2. 如果不保留,那么 t [ j ] t[j] t[j]可能失配,也可能与 s [ i − 2 x ] , x ∈ { 1 , 2 , 3 , . . . . } s[i-2x], x\in\{1,2,3,....\} s[i2x],x{1,2,3,....}匹配。如果失配,自然不保留的方案就比保留的方案劣。如果不失配,那么 t [ j − 1 ] t[j-1] t[j1]将会在 s [ i − 2 x − 1 − 2 k ] , k ∈ { 0 , 1 , 2 , . . . . } s[i-2x-1-2k],k\in\{0,1,2,....\} s[i2x12k],k{0,1,2,....}中匹配,这个范围是比保留方案中的范围要小的。
    因此,选择一但匹配就保留的方案是一定优于不保留的方案的。

于是给出算法:从后往前匹配,如果失配, i − = 2 i-=2 i=2;如果匹配, i − − i-- i.

#include <iostream>
#include <string.h>
using namespace std;
char s[200100], t[200100];
int main()
{
    int T; cin >> T;
    while(T--)
    {
        scanf("%s %s",s,t);
        int n = strlen(s), m = strlen(t);
        int j, i = n-1;
        for(j = m-1; j>=0;)
        {
            if(i<0) break;
            if(s[i] == t[j]) 
            {
                i--;
                j--;
            }
            else i-=2;
        }
        if(j < 0) printf("YES\n");
        else printf("NO\n");
    }
    // system("pause");
}

E. Permutation Shift

分析:
观察发现,当n确定,k不同时,任意一个数字x的位置i的一定不相同。
也就是说,给定一个数字x和该数字的位置i,就可以计算出这种放置下的k,记数字x和位置i所贡献的k值为 k x k_x kx
同时观察到,做一次两个元素的交换,会改变两个数字的位置,也就是会改变两个可能的k值,
也就是说,如果要将一个 k = a k=a k=a的排列转换成 k = b k=b k=b的排列,至少要做 n 2 \frac{n}{2} 2n次交换(也就是题中所说的操作二)
注意到题目给的操作二的限制次数是m次,而且 m ≤ ⌊ n 3 ⌋ m\le\lfloor \frac{n}{3} \rfloor m3n
因此只有满足一定条件的k值才有可能在m次操作下使得所有元素的贡献k值相等。
记给定数组a中每个元素 a i a_i ai的贡献k值为 k i k_i ki, 记 b j b_j bj的值为 k i = j k_i=j ki=j的出现次数,
具体来说k值所需要满足的条件就是 n − b j ≤ 2 ∗ m ,   b j = ∑ i = 1 n a i = = j n-b_j\le2*m, \space b_j=\sum_{i=1}^na_i==j nbj2m, bj=i=1nai==j

不难发现,满足条件的k值至多不超过3个,但上述条件是必要条件,而不是充要条件,我们还需要检查满足条件的k值到底能不能符合题意。
不难想到 O ( n ) O(n) O(n)的检查方法,因为满足条件的k值不超过3个,所以复杂度是安全的。

#include <iostream>
#include <algorithm>
using namespace std;
int a[300100];
int b[300100];
int ans = 0;
bool vis[300100];
void init(int n)
{
    for(int i = 0; i <= n; i++) vis[i] = b[i] = 0;
    ans = 0;
}

bool check(int n, int k, int m)
{
    int cnt = 0;
    for(int i = 0; i <= n; i++) vis[i] = 0;
    for(int i = 1; i <= n; i++)
    {
        if(vis[i] == 0)
        {
            if(i == (a[i]+k)%(n+1)+(a[i]+k)/(n+1) ) vis[i] = 1;
            else 
            {
                int head = i;
                int p = (a[i]+k)%(n+1)+(a[i]+k)/(n+1);
                while(p != i)
                {
                    vis[p] = 1; 
                    p = (a[p]+k)%(n+1)+(a[p]+k)/(n+1);
                    cnt++; if(cnt > m) return 0;
                }
                vis[i] = 1;
            }
        }
    }
    return 1;
}
int main()
{
    int t; cin >> t;
    while(t--)
    {
        int n, m; cin >> n >> m;
        init(n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
            int x = i-a[i], y = n-a[i]+i;
            if(x >= 0) x %= n, b[x]++;
            if(y >= 0 && x<0) y %= n, b[y]++;
        }
        
        int aa[5], top = 0;
        for(int i = 0; i < n; i++)
            if(n-b[i]<=m*2 && check(n,i,m)) aa[top++] = i;  

        printf("%d",top);
        for(int i = 0; i < top; i++) printf(" %d",aa[i]); printf("\n");
    }
    // system("pause");
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值