AtCoder Beginner Contest 215 A~F 题解

本场链接:AtCoder Beginner Contest 215

闲话

感觉有些不在状态,傻逼题都做不出来,脑子想不进去.GH不知道能不能做,能做再说.

A - Your First Judge

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

int main()
{
    string s;cin >> s;
    if(s == "Hello,World!") cout << "AC
";
    else cout << "WA
";
    return 0;
}

B - log2(N)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

int main()
{
    ll n;cin >> n;
    int res = 0;
    forn(k,0,62)    if((1ll << k) <= n) res = k;
    printf("%d
",res);
    return 0;
}

C - One More aab aba baa

注意去重.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 100;
char s[N];
int d[N];

int main()
{
    int k;scanf("%s%d",s + 1,&k);int n = strlen(s + 1);
    forn(i,1,n) d[i] = i;

    vector<string> all;

    do
    {
        string cur;
        forn(i,1,n) cur += s[d[i]];
        all.push_back(cur);    
    } while (next_permutation(d + 1,d + n + 1));
    
    sort(all.begin(),all.end());all.erase(unique(all.begin(),all.end()),all.end());
    cout << all[k - 1] << endl;
    return 0;
}

D - Coprime 2

正难则反:考虑所有会有(gcd(a_i,k) = d)(d eq 1)(k).显然这样的(k)(a_i)的一个约数(d)的若干倍.那么枚举出所有(a_i)的约数,并且把这些约数自己和所有约数的倍数全部打上标记,被标记的点就是存在(gcd(a_i,k) = d)(d eq 1)(k).如此,统计最后没有被标记的数即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 1e5+7;
bool st[N];

int main()
{
    int n,m;scanf("%d%d",&n,&m);
    forn(i,1,n)
    {
        int a;scanf("%d",&a);
        for(int d = 1;1ll * d * d <= a;++d)
        {
            if(a % d != 0)  continue;
            st[d] = 1;
            st[a / d] = 1;
        }
    }

    st[1] = 0;
    
    forn(i,2,N - 1)
    {
        if(!st[i])  continue;
        for(int j = i;j <= N - 1;j += i)    st[j] = 1;
    }

    vector<int> res;
    forn(i,1,m) if(!st[i])  res.push_back(i);

    printf("%d
",(int)res.size());
    for(auto& _ : res)  printf("%d
",_);
    return 0;
}

E - Chain Contestant

因为$ |sum| = 10$考虑状压:

  • (f[i][j][k])表示做完第(i)个位置,当前已经选出的元素集合是(j)且末尾元素是(k)的方案数.
  • 入口:(f[i][(1 << s_i)][s_i] = 1).
  • 转移:若不放入 (s_i) 显然 $ += f[i - 1][j][k]$ ;若放入 (s_i),可能会有两种情况:要么(s_i)是第一次出现在序列里,作为起点;要么(s_i)是接到当前已经有的屁股后面,显然这个已经有的必须是当前的末尾.如果是接到后面,让(f[i][j][k] += f[i - 1][j][k]),此时需要满足(k == j).反过来如果(k)不属于(j)集合,那么此时放入(s_i)就是作为一个新的起点:(f[i][j | (1 << s_i)][k] += sum_{z eq k} f[i - 1][j][z]).(在具体实现的时候,如果转移方程是正确的,那么(z == k)的时候也不会有问题,因为(j)里面不包含(k)的情况下方案数是(0))
  • 出口:(res = sum_{j,k} f[n][j][k]).

入口可以不直接写出,在转移时给加一即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 10,M = (1 << 10),C = 1020,MOD = 998244353;
int f[C][M][N];
char s[C];

int main()
{
    int n;scanf("%d",&n);
    scanf("%s",s);

    forn(i,1,n)
    {
        int x = s[i - 1] - 'A';
        forn(j,0,(1 << 10) - 1) forn(k,0,9)
        {
            f[i][j][k] = f[i - 1][j][k];
            if(k == x)  f[i][j][k] = (1ll * f[i][j][k] + f[i - 1][j][k]) % MOD;
        }
        
        forn(j,0,(1 << 10) - 1)
        {
            if(j >> x & 1)  continue;
            forn(k,0,9) f[i][j | (1 << x)][x] = (1ll * f[i][j | (1 << x)][x] + f[i - 1][j][k]) % MOD;
        }

        f[i][1 << x][x] = (f[i][1 << x][x] + 1) % MOD;
    }

    int res = 0;
    forn(j,0,(1 << 10) - 1) forn(k,0,9)  res = (res + f[n][j][k]) % MOD;
    printf("%d
",res);
    return 0;
}

F - Dist Max 2

使最小值最大,考虑二分答案:二分最终答案(ans geq L)?

(ans geq L) 等价于 (min(|x_i - x_j|,|y_i - y_j|) geq L),即 (|x_i - x_j| geq L,|y_i - y_j| geq L) 同时满足.

如此,check函数等价于求解:对于每个位置 (i in [1,n - 1]),是否存在一个 (j in [i + 1,n]) 同时满足如上两个条件.如果把绝对值拆掉,可以得到四个不等式,满足其中一个不等式即可.由于((i,j))的相对次序可以忽略,可以将所有点按照 (x) 坐标上升排序,这样可以删掉其中两个,最后剩下: (x_j - x_i geq L && y_i - y_j geq L)(x_j - x_i geq L && y_j - y_i geq L) 满足其一即可.

排序之后保证了一维偏序关系,剩下的一维偏序关系可以发现只需要找到:在某个后缀中(y)的最大值或最小值,因为要看是否存在一个 (j in [i + 1,n]) 满足 (y_j - y_i geq L) 那肯定取 (y_j) 的最大值.分别记作 (Rmin/Rmax).即可将 check 的复杂度做到 (O(nlogn)).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second

const int N = 2e5+7;
pii p[N];
int Rmin[N],Rmax[N];
int n;

bool check(int L)
{
    forn(i,1,n - 1)
    {
        int pos = lower_bound(p + i + 2,p + n + 1,pii{p[i].x + L,0}) - p;
        if(pos == n + 1)    continue;
        if(Rmax[pos] >= L + p[i].y || Rmin[pos] <= p[i].y - L)  return 1;
    }
    return 0;
}

int main()
{
    scanf("%d",&n);
    forn(i,1,n) scanf("%d%d",&p[i].x,&p[i].y);
    sort(p + 1,p + n + 1);

    Rmin[n] = p[n].y;Rmax[n] = p[n].y;
    forr(i,1,n - 1) Rmin[i] = min(Rmin[i + 1],p[i].y),Rmax[i] = max(Rmax[i + 1],p[i].y);

    int l = 0,r = 2e9,idx = 0;
    while(l < r)
    {
        int mid = l + r + 1 >> 1;
        if(check(mid))  l = mid;
        else r = mid - 1;
    }
    printf("%d
",l);
    return 0;
}

原文地址:https://www.cnblogs.com/HotPants/p/15173207.html