Codeforces Round #605(Div3)A~E

Codeforces Round #605(Div3)A~E

A. Three Friends

题意:

  • 给三个数\(a,b,c\),对每一个数字可以进行一次操作,加一减一或者不变。
  • 求三个数两两组合的差值绝对值的最小值。

思路:

  • 先排个序。
  • 假设排序后三个数从小到大是\(a,b,c\)
  • 那么他们的两两差就是\((c-b)+(c-a)+(b-a)=2c-2a\)
  • 为了让他们的\(2c-2a\)最小,那就缩小\(c\),增大\(a\)
#include
using namespace std;
void solve()
{
    int a[3];
    for(int i = 0; i < 3; i++)
        cin >> a[i];
    sort(a, a+3);
    if(a[0] < a[2]) a[0]++;
    if(a[2] > a[0]) a[2]--;
    cout << 2*(a[2]-a[0]) << endl;
}

int main()
{
    int T; scanf("%d", &T);
    while(T--) solve();
    return 0;
}

B. Snow Walking Robot

题意:

  • 给出一串指令,最后回到原点,除了起点之外同一个地方不能经过两次。
  • 你可以删除指令集中的一些指令或重组这些指令,问最多能走多少步。

思路:

  • 构造题。
  • 首先可以发现,L和R,U和D之间是可以相互抵消的。
  • 那么我们只需要从这两个数字中取min即可。
  • 剩下的让他们绕一个正方形。
  • 值得注意的是,如果上下或左右某一方没有的时候,另一边只能为1,因为一个格子不能走两次。
#include
using namespace std;

void solve()
{
    string str;
    cin >> str;
    int l, r, u, d;
    l = r = u = d = 0;
    for(auto x : str)
    {
        if(x == 'L') l++;
        if(x == 'R') r++;
        if(x == 'U') u++;
        if(x == 'D') d++;
    }
    int num1 = min(l, r);
    int num2 = min(u, d);

    if(num1 == 0 || num2 == 0)
    {
        num1 = min(num1, 1);
        num2 = min(num2, 1);
    }

    string ans = "";
    for(int i = 1; i++ <= num1;)
        ans += 'L';
    for(int i = 1; i++ <= num2;)
        ans += 'U';
    for(int i = 1; i++ <= num1;)
        ans += 'R';
    for(int i = 1; i++ <= num2;)
        ans += 'D';
    cout << ans.size() << endl;
    cout << ans << endl;
}

int main()
{
    int T; scanf("%d", &T);
    while(T--) solve();
    return 0;
}

C. Yet Another Broken Keyboard

题意:

  • 给你一个字符串,小明想要练习自己的打字速度,所以他打算输入所有的子串。
  • 但是键盘坏了,有部分键不能使了,问在这种情况下,小明还能输入多少个子串。

思路:

  • 对于一个字符串而言,他有\(\frac{n(n+1)}{2}\)个子串。
  • 那么可以把原先的字符串拆解成可以用键盘输入的几个字符串,然后直接计算答案就行。
#include
using namespace std;
int n, k;
int vis[30]; //可以用的键位
string str;

int main()
{
    int n, k;
    cin >> n >> k;
    cin >> str;
    for(int i = 1; i <= k; i++)
    {
        char c[2];
        scanf("%s", c);
        vis[c[0]-'a']++;
    }
    //有溢出风险, 记得开longlong
    long long ans = 0;
    for(int i = 0; i < n; i++)
    {
        if(vis[str[i]-'a']) //如果当前位置可以
        {
            long long num = 0; //统计连续的可以的串
            for(int j = i; j < n; j++)
            {
                i = j;
                if(vis[str[j]-'a']) num++;
                else break;
            }
            ans += num*(num+1) / 2;
        }
    }
    cout << ans << endl;
    return 0;
}

D. Remove One Element

题意:

  • 给你一个长度为n的序列a。
  • 你可以最多从序列中删除一个数字,最终的序列长度为\(n-1\)或者\(n\)
  • 你需要找到最长的可能的严格上升的子序列(这个子序列是连续的)。

思路:

  • 假如说没有删除一个数字的条件的话,这题应该怎么写?
  • \(f(i)\)表示前\(i\)个数字中,最长的严格上升的子序列长度为多少。
  • 那么有递推方程:

    • \(if(a(i)>a(i-1))\ f(i)=f(i-1)+1,else\ f(i)=1\)
  • 现在要删除某个数字,让序列变长,假设说删除的是第\(i\)号位置上的数字,那么如果\(a(i+1)>a(i-1)\),就可以快速的计算出\(i-1\)前面有多少个连续的严格上升的数字,但是后面的就不是很好计算,是\(O(n)\)的。
  • 所以设\(g(i)\)表示以第\(i\)个数字为开头,最多能有多长的合法子序列。
  • 枚举删除位置,用\(f(i-1)+g(i+1)\)更新答案。

#include
using namespace std;
const int maxn = 2e5 + 10;
int n, a[maxn], ans;
int f[maxn], g[maxn];
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);

    f[1] = ans = 1;
    for(int i = 2; i <= n; i++)
    {
        f[i] = 1;
        if(a[i] > a[i-1]) f[i] = f[i-1]+1;
        ans = max(f[i], ans);
    }
    
    g[n] = 1;
    for(int i = n-1; i >= 1; i--)
    {
        g[i] = 1;
        if(a[i] < a[i+1]) g[i] = g[i+1]+1;
    }
    ans = max(ans, g[1]);
    for(int i = 2; i <= n-1; i++)
        if(a[i+1]>a[i-1])
        ans = max(ans, f[i-1]+g[i+1]);
    ans = max(ans, f[n]);
    cout << ans << endl;
    return 0;
}

E. Nearest Opposite Parity

题意:

  • 有一个长度为n的序列a。
  • 每一步你可以从第\(i\)个位置跳跃到第\(i-a_i\)\(i+a_i\)。(当然\(1\leq i-a_i,i+a_i\leq n\))。
  • 对于每一个\(i\),你想知道他到\(j\)的最小步数,其中\(a_i,a_j\)奇偶性不同。

思路:

  • 每个点可以跳到另一个点,所以不妨把整张图看成有\(n\)个点,至多有\(2n\)条边的有向图。

  • 首先根据输入的数据进行建图。如果一个点\(x\)可以到\(y\),那么就建立一条从\(y\)\(x\)的边。
  • 之后进行bfs。
  • 先加入偶数点入队列,就相当于以偶数点为终点(因为是反向建图),去找最近的可以到他的奇数点,同时更新答案。
  • 奇数点同理。

#include
using namespace std;
const int maxn = 2e5 + 10;
int a[maxn], n;

int head[maxn], nex[maxn<<2], ver[maxn<<2], tot;
inline void add_edge(int x, int y){
    ver[++tot] = y; nex[tot] = head[x]; head[x] = tot;
}

int path[maxn][3];

void bfs(int dir)
{
    queue q;
    for(int i = 1; i <= n; i++)
    {
        if(a[i] % 2 == dir)
        {
            path[i][dir] = 0;
            q.push(i);
        } else {
            path[i][dir] = -1;
        }
    }

    while(q.size())
    {
        int x = q.front(); q.pop();
        for(int i = head[x]; i; i = nex[i])
        {
            int y = ver[i];
            if(path[y][dir] == -1)
            {   //代表是从奇偶性不同的点转移过来的
                path[y][dir] = path[x][dir]+1;
                q.push(y);
            }
        }
    }
}


int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++)
    {
        if(i + a[i] <= n) add_edge(i+a[i], i);
        if(i - a[i] >= 1) add_edge(i-a[i], i);
    }

    bfs(0); bfs(1);
    for(int i = 1; i <= n; i++)
    {
        if(a[i] % 2) printf("%d ", path[i][0]);
       else printf("%d ", path[i][1]);
    }
    return 0;
}

你可能感兴趣的:(Codeforces Round #605(Div3)A~E)