AGC选做

 ABCDEF
AGC043OO    
AGC044      
AGC045O     
AGC046      
AGC047      
AGC048O     
AGC049OO    
AGC050OOO   
AGC051 O    
AGC052O     
AGC053 O    


 

 

 

 

 

 

 

 

 

 

 

 

AGC043

A - Range Flip Find Route

对于一个路径来说,连续的#段数就是这条路径的最小操作次数。dp算这个东西就行了。

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n , m ;
    cin >> n >> m ;
    vector<string> s(n) ;
    for(int i = 0 ; i < n ; i ++)  cin >> s[i] ;
    vector<vector<int>> dp(n , vector<int>(m , 1000000)) ;
    dp[0][0] = (s[0][0] == '#') ;
    for(int i = 0 ; i < n ; i ++)  for(int j = 0 ; j < m ; j ++)
    {
        if(i > 0)
        {
            if(s[i - 1][j] == '.' && s[i][j] == '#')  dp[i][j] = min(dp[i][j] , dp[i - 1][j] + 1) ; 
            else  dp[i][j] = min(dp[i][j] , dp[i - 1][j]) ;
        }
        if(j > 0)
        {
            if(s[i][j - 1] == '.' && s[i][j] == '#')  dp[i][j] = min(dp[i][j] , dp[i][j - 1] + 1) ;
            else  dp[i][j] = min(dp[i][j] , dp[i][j - 1]) ;
        }
    }
    cout << dp[n - 1][m - 1] << '\n' ;
    return 0 ;
}

 

B - 123 Triangle

首先转为0,1,2考虑问题,然后进行分类讨论。

  • 如果序列中存在1,答案是01。那么序列的0,2等价,都可以认为是01是改变答案奇偶性的关键。
  • 如果序列中不存在1,答案是02。那么可以认为212是改变答案奇偶性的关键。

那么查看1在序列中的位置,从而判断出该位置到达终点位置的路径的奇偶性即可。

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n ;
    cin >> n ;
    string s ;
    cin >> s ;
    vector<int> a(n - 1) ;
    for(int i = 0 ; i < n - 1 ; i ++)  a[i] = abs(s[i] - s[i + 1]) ;
    if(n == 2)
    {
        cout << a[0] << '\n' ;
        return 0 ;
    }
    auto C = [&](int n , int m)
    {
        return (n & m) == m ;
    } ;
    int cnt0 = 0 , cnt1 = 0 , cnt2 = 0 ;
    bool f1 = false ;
    bool f2 = false ;
    for(int i = 0 ; i < n - 1 ; i ++)
    {
        if(a[i] == 0)  cnt0 ++ ;
        else if(a[i] == 1)  cnt1 ++ , f1 ^= C(n - 2 , i) ;
        else  cnt2 ++ , f2 ^= C(n - 2 , i) ;
    }
    if(cnt1 > 0)  cout << f1 << '\n' ;
    else  cout << 2 * f2 << '\n' ;
    return 0 ;
}

 

AGC045

A - Xor Battle

需要想到这个题本质是对于当前的1,是否可以用后面的0将它的影响消除。

倒序考虑。对0的后缀做线性基。如果当前是1且对应的a_i不能插入线性基,则有必胜策略。

#include<bits/stdc++.h>
using namespace std ;
struct Linear_base
{
    bool zero ;
    int cnt ;
    long long b[75] , p[75] ;
    void init()
    {
        zero = 0 ;
        cnt = 0 ;
        memset(b , 0 , sizeof(b)) ;
        memset(p , 0 , sizeof(p)) ;
    }
    bool insert(long long x)
    {
        for(int i = 60 ; i >= 0 ; i --)
        if(x & (1ll << i))
        {
            if(b[i])  x ^= b[i] ;
            else  
            {
                b[i] = x ;
                return 1 ;
            }
        }
        zero = 1 ;
        return 0 ;
    }
} linear_base ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    cin >> T ;
    while(T --)
    {
        int n ;
        cin >> n ;
        vector<long long> a(n) ;
        for(int i = 0 ; i < n ; i ++)  cin >> a[i] ;
        string s ;
        cin >> s ;
        linear_base.init() ;
        int flag = 0 ;
        for(int i = n - 1 ; i >= 0 ; i --)
        {
            if(s[i] == '0')  linear_base.insert(a[i]) ;
            else
            {
                if(linear_base.insert(a[i]))  
                {
                    flag = 1 ;
                    break ;
                }
            }
        }
        cout << flag << '\n' ;
    }
    return 0 ;
}

 

AGC048

A - atcoder < S

这道题比较中国风,只要固定前缀模拟一下即可。注意前缀可以是"atcoder"

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    cin >> T ;
    while(T --)
    {
        string s ;
        cin >> s ;
        vector<list<int>> pos(26) ;
        int n = s.size() ;
        for(int i = 0 ; i < n ; i ++)
        {
            int x = s[i] - 'a' ;
            pos[x].push_back(i) ;
        }
        int ans = 1e9 ;
        string t = "atcoder" ;
        vector<int> tmp(26 , -1) ;
        int sum = 0 ;
        int cnt = 0 ;
        for(int i = 0 ; i < 7 ; i ++)
        {
            int x = t[i] - 'a' ;
            for(int j = x + 1 ; j < 26 ; j ++)
            {
                if(!pos[j].empty())
                {
                    int now = pos[j].front() ;
                    int f = now ;
                    for(auto u : tmp)  if(u > f)  now ++ ;
                    ans = min(ans , sum + now - i) ;
                }
            }
            if(!pos[x].empty())
            {
                int now = pos[x].front() ;
                tmp[x] = now ;
                int f = now ;
                for(auto u : tmp)  if(u > f)  now ++ ;
                sum += now - i ;
                pos[x].pop_front() ;
            }
            else  break ;
            cnt ++ ;
        }
        if(cnt == 7 && s.size() > 7)
        {
            ans = min(ans , sum) ;
        }
        if(ans > 1e8)  cout << "-1\n" ;
        else  cout << ans << '\n' ;
    }
    return 0 ;
}

 

AGC049

A - Erasing Vertices

第一反应是缩点然后在DAGdp,但是想了想觉得代码太长了,他们3分钟就过了,而且atcoder不太可能考这种题。

但是没想到什么简单做法。看题解才明白要把期望拆开,转成每个点的期望次数求和。

S[i]表示可以到达点i的点数,答案是\sum \frac{1}{S[i]}

#include<bits/stdc++.h>
using namespace std ;
const int maxn = 100 + 10 ;
int dis[maxn][maxn] ;
int S[maxn] ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n ;
    cin >> n ;
    memset(dis , 0x3f , sizeof(dis)) ;
    for(int i = 1 ; i <= n ; i ++)
    {
        string s ;
        cin >> s ;
        for(int j = 0 ; j < n ; j ++)
            if(s[j] == '1')  dis[i][j + 1] = 1 ;
        dis[i][i] = 0 ;
    }
    for(int k = 1 ; k <= n ; k ++)
        for(int i = 1 ; i <= n ; i ++)
            for(int j = 1 ; j <= n ; j ++)
                dis[i][j] = min(dis[i][j] , dis[i][k] + dis[k][j]) ;
    for(int i = 1 ; i <= n ; i ++)  for(int j = 1 ; j <= n ; j ++)
        if(dis[i][j] <= 100)  S[j] ++ ;
    double ans = 0.0 ;
    for(int i = 1 ; i <= n ; i ++)  ans += 1.0 / S[i] ;
    cout << fixed << setprecision(12) << ans << '\n' ;
    return 0 ;
}

 

B - Flip Digits

转化为判定前缀异或和是否相同。这个操作就是当s_i \neq s_{i + 1}时进行赋值s_i = s_{i+1}操作。从左往右扫一遍即可。

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n ;
    cin >> n ;
    string s , t ;
    cin >> s >> t ;
    vector<int> a(n + 1 , 0) , b(n + 1 , 0) ;
    for(int i = 1 ; i <= n ; i ++)
    {
        a[i] = a[i - 1] ^ (s[i - 1] - '0') ;
        b[i] = b[i - 1] ^ (t[i - 1] - '0') ;
    }
    int j = 1 ;
    long long ans = 0 ;
    for(int i = 1 ; i <= n ; i ++)
    {
        j = max(j , i) ;
        while(j + 1 <= n && a[j + 1] == a[j])  j ++ ;
        if(a[j] != b[i])
        {
            j ++ ;
            if(j > n)
            {
                cout << "-1\n" ;
                return 0 ;
            }
            else
            {
                ans += j - i ;
            }
        }
    }
    cout << ans << '\n' ;
    return 0 ;
}

 

AGC050

A - AtCoder Jumper

考虑重新编号[0...n-1]。结论是编号i指向(i*2)%n,(i*2+1)%n

因为i10步后可以走到(1024*i+j)%n,其中0\leqslant j < 1024

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n ;
    cin >> n ;
    vector<array<int , 2>> v(n) ;
    for(int i = 0 ; i < n ; i ++)  v[i][0] = i * 2 % n , v[i][1] = (i * 2 + 1) % n ;
    for(int i = 0 ; i < n ; i ++)  cout << v[i][0] + 1 << ' ' << v[i][1] + 1 << '\n' ;
    return 0 ;
}

 

B - Three Coins

考虑dp[i][j]表示在只在[i,j]这段区间操作的情况下的最大权值和。

有如下3种情况。

  • 100010001 容易发现这三个1中间有3的倍数个0
  • 0111 左端点右移
  • 1110111 直接按照区间dp的套路划分区间。
#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n ;
    cin >> n ;
    vector<int> a(n) ;
    vector<vector<int>> dp(n , vector<int>(n , 0)) ;
    for(int i = 0 ; i < n ; i ++)  cin >> a[i] ;
    int ans = 0 ;
    for(int len = 3 ; len <= n ; len ++)
        for(int i = 0 ; i < n ; i ++)
        {
            int j = i + len - 1 ;
            if(j >= n)  break ;
            for(int k = i ; k < j ; k ++)
            {
                //100010001
                if((k - 1 - i) % 3 == 0 && (j - 1 - k) % 3 == 0)
                    dp[i][j] = max(dp[i][j] , dp[i + 1][k - 1] + dp[k + 1][j - 1] + a[i] + a[k] + a[j]) ;
                //0111
                dp[i][j] = max({dp[i][j] , dp[i + 1][j] , dp[i][j - 1]}) ;
                //1110111
                dp[i][j] = max(dp[i][j] , dp[i][k] + dp[k + 1][j]) ;
            }
            ans = max(ans , dp[i][j]) ;
        }
    cout << ans << '\n' ;
    return 0 ;
}

 

C - Block Game

题目问成功的方案数。正难则反,考虑失败的方案数。

容易发现对于一个序列中的连续S段,这些段长度分别是a_1,a_2,\cdots ,a_k,失败需要满足a_1 \geqslant 2^{k-1} , a_2 \geqslant 2^{k-2} , \cdots , a_k \geqslant 1

dp[i][j]表示[i+1,n]这段区间出现jS的字符串种类数。

转移有那么一丢丢麻烦,虽然很短。

#include<bits/stdc++.h>
using namespace std ;
const int mod = 998244353 ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    //dp[i][j]表示以[i + 1 , n]有j段合法S的字符串种类数
    string s ;
    cin >> s ; //s[1...n]
    int n = s.size() ;
    vector<int> pre(n + 1 , 0) ;
    for(int i = 1 ; i <= n ; i ++)  pre[i] = pre[i - 1] + (s[i - 1] == 'B') ;
    vector<vector<int>> dp(n + 1 , vector<int>(22 , 0)) ;
    dp[n][0] = 1 ;
    for(int i = n ; i >= 1 ; i --)
        for(int j = 0 ; j <= 20 ; j ++)
        {
            if(s[i - 1] == 'S' || s[i - 1] == '?')
            {
                dp[i - 1][j] += dp[i][j] ;
                dp[i - 1][j] %= mod ;
            }
            if(s[i - 1] == 'B' || s[i - 1] == '?')
            {
                int nxt = max(0 , i - (1 << j) - 1) ;
                if(pre[i - 1] - pre[nxt] == 0)
                {
                    dp[nxt][j + 1] += dp[i][j] ;
                    dp[nxt][j + 1] %= mod ;
                }
            }
        }
    int ans = 1 ;
    for(int i = 0 ; i < n ; i ++)  if(s[i] == '?')  ans = ans * 2 % mod ;
    for(int i = 0 ; i <= 21 ; i ++)  ans -= dp[0][i] , ans %= mod ;
    ans = (ans + mod) % mod ;
    cout << ans << '\n' ;
    return 0 ;
}

 

AGC051

B - Bowling

点集\left \{ {i*\vec{p}+j*\vec{q}+k*\vec{r}}\right \}(1\leqslant i,j,k \leqslant 10)

其中\vec{p},\vec{q},\vec{r}是平行于A,B,C观测方向的长度随机的向量。

A,B,C分别至多看到\frac{1}{10}的点集。

题解说的这个随机比较神,我理解是为了使得D的期望是A,B,C10倍。

我并没有随机长度,而是把长度设置的分隔比较大,也能达到同样的效果。

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int x1 = 100 , y1 = 0 ;
    int x2 = 1000 , y2 = 1000 ;
    int x3 = 0 , y3 = 10000000 ;
    cout << "1000\n" ;
    for(int i = 1 ; i <= 10 ; i ++)
        for(int j = 1 ; j <= 10 ; j ++)
            for(int k = 1 ; k <= 10 ; k ++)
                cout << i * x1 + j * x2 + k * x3 << ' ' << i * y1 + j * y2 + k * y3 << '\n' ;
    return 0 ;
}

 

AGC052

A - Long Common Subsequence

依次输出0n1n0即可。

s+s出现的2*n0下标分别是a_1,a_2,...,a_n,\cdots,a_{n+1},a_{n+2},\cdots,a_{2*n}。因为ss+s的循环节,所以a_n,a_{2*n}两者之间有n1

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    cin >> T ;
    while(T --)
    {
        int n ;
        cin >> n ;
        string s ;
        cin >> s ;
        cin >> s ;
        cin >> s ;
        cout << '0' ;
        for(int i = 1 ; i <= n ; i ++)  cout << '1' ;
        for(int i = 1 ; i <= n ; i ++)  cout << '0' ;
        cout << '\n' ;
    }
    return 0 ;
}

 

AGC053

B - Taking the middle

有一个结论:前i轮后手选择的i个数位于[n-i+1,n+i]

最小化后手选择的数的和即可。

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    // [n - i + 1 , n + i]
    int n ;
    cin >> n ;
    vector<int> a(2 * n + 1) ;
    for(int i = 1 ; i <= 2 * n ; i ++)  cin >> a[i] ;
    long long ans = 0 ;
    for(int i = 1 ; i <= 2 * n ; i ++)  ans += a[i] ;
    priority_queue<int> q ;
    for(int i = 1 ; i <= n ; i ++)
    {
        q.push(-a[n - i + 1]) ;
        q.push(-a[n + i]) ;
        ans -= abs(q.top()) ;
        q.pop() ;
    }
    cout << ans << '\n' ;
    return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值