AtCoder Beginner Contest 261 A-G

AtCoder Beginner Contest 261 A-G

https://atcoder.jp/contests/abc261

A - Intersection

题意

给两个区间,求相交的长度

分析

如题
注意考虑清楚各种情况
比如区间相交,包含,不相交

Code

#include <bits/stdc++.h>

using namespace std;
typedef pair<int, int> pii;

int main () {
    //int a, b, c, d;
    pii a[2];
    cin >> a[0].first >> a[0].second >> a[1].first >> a[1].second;
    sort (a, a + 2);
    if (a[1].second <= a[0].second) cout << a[1].second-a[1].first;
    else    cout << max(a[0].second-a[1].first,0);
    
}

B - Tournament Result

题意

给定字符矩阵A, \(A_{i,j}\) 为 'W' 表示 i 赢了 j,为 'L' 表示 j 赢了 i, 为 'D' 表示平局
判断i,j的对战结果是否有矛盾存在

分析

直接看对称处的结果是否有矛盾结果,即比较 \(A_{i,j}\)\(A_{j,i}\)是否矛盾

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 1005;
char a[N][N];

int main () {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++)
            cin >> a[i][j];

    for (int i = 1; i <= n; i ++)
        for (int j = 1; j < i; j ++) {
            if (a[i][j] == 'D') {
                if (a[j][i] != a[i][j]) {
                    cout << "incorrect";
                    return 0;
                }
            }
            else {
                if (a[i][j] == 'W') {
                    if (a[j][i] != 'L') {
                        cout << "incorrect";
                        return 0;
                    }                
                }
                if (a[i][j] == 'L') {
                    if (a[j][i] != 'W') {
                        cout << "incorrect";
                        return 0;
                    }                
                }                
            }
        }
    
    cout << "correct";
}

//坐标成对
//可能出现L D相对

C - NewFolder(1)

题意

输入n个字符串,如果该串在之前出现过x(x>0)次,则输出串+'(' + x + ')',否则直接输出串

分析

语法题,直接来个map

Code

#include <bits/stdc++.h>

using namespace std;
map<string, int> mp;

int main () {
    int n;
    cin >> n;
    while (n --) {
        string s;
        cin >> s;        
        cout << s;
        if (mp[s])  cout << '(' << mp[s] << ')';
        cout << endl;
        mp[s] ++;
    }
    
}

D - Flipping and Bonus

题意

现投掷1枚硬币n次,可能的结果为:

  1. value++, money+=x[i];
  2. value=0, money=0;
    另,value达到ci时,money+=yi
    求投掷n次硬币可能达到的最大money为多少

分析

简单dp,直接来个状态转移
f[i][j]: 当前走到i, 有连续j次正面朝上的money
然后按照第i次正面朝上/朝下,分别转移即可

Code

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int N = 5005;
int n, m;
int v[N], x[N];
int f[N][N]; //前i次掷骰子的连胜次数为j的最大得分

signed main () {
    cin >> n >> m;
    for (int i = 1; i <= n; i ++)   cin >> x[i];
    
    for (int i = 1; i <= m; i ++) {
        int a, b;
        cin >> a >> b;
        v[a] = b;
    }

    for (int i = 1; i <= n; i ++) {
        //第i次正面朝上
        for (int j = 0; j <= i; j ++) 
            f[i][j] = f[i-1][j-1] + x[i] + v[j];  
        
        //第i次正面朝下
        //在i次之前所有可能的情况中,记录最大值
        for (int j = 0; j < i; j ++)
            f[i][0] = max (f[i][0], f[i-1][j]);
    }

    int ans = 0;
    for (int i = 0; i <= n; i ++)   ans = max (ans, f[n][i]);
    cout << ans << endl;
}

//1. value++, money+=x[i];
//2. value=0, money=0;
//value达到ci时,money+yi

//dp
//max (f[n][0], f[n][1],...,f[n][n]);
//f[i][j]: 当前走到i, 有连续j次正面朝上的money

E - Many Operations

题意

一共n轮操作,每一次操作输入op和a:

  1. op == 1, x &= a;
  2. op == 2, x |= a;
  3. op == 3, x ^= a;

第i轮操作时操作,将进行\(1, 2, 3, ..., i\)次操作(前缀操作)
询问每一轮操作后的x值,x不断更新

分析

位独立 + 要么0要么1
其实就是函数规则的复合

(类似"走地图")

所以可以分别以0为起点/以1为起点,来迭代更新

Code

#include <bits/stdc++.h>
#define int long long

using namespace std;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
int n, x;
pii p[N];

signed main () {   
    cin >> n >> x;
    int m = pow(2,30)-1;
    int s1 = m, s0 = 0;
    while (n --) {
        int op, y;
        cin >> op >> y;
        if (op == 1)    s1 &= y, s0 &= y;
        else if (op == 2)    s1 |= y, s0 |= y;
        else    s1 ^= y, s0 ^= y;
        
        x = (x & s1) | ((x^m)&s0);
        cout << x << endl;
    }
}

//模拟该过程

F - Sorting Color Balls

题意

给定n个元素,每个元素有值和颜色两种属性,每次操作可以交换相邻的两个元素,若这两个元素颜色不同,则需要消耗一点cost,求这n个元素按从小到大排序所需要的最小cost

分析

经典的求逆序对题,本题就是要求不同颜色的逆序对的个数
正难则反,此问题可以转化为求逆序对个数,然后再枚举每一种颜色,求相同颜色的逆序对的个数,二者相减即可。
BIT求逆序对,注意每次求完之后要消除影响(重置BIT)

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int N = 3e5 + 5;
int c[N], x[N];

//BIT模板
template <typename T>
struct BIT {
    int n;
    vector<T> a;
    BIT(int n) : n(n), a(n) {}
    void add(int x, T v) {
        for (int i = x + 1; i <= n; i += i & -i) 
            a[i - 1] += v;
    }
    T sum(int x) {
        T ans = 0;
        for (int i = x; i > 0; i -= i & -i) 
            ans += a[i - 1];
        return ans;
    }
    T rangeSum(int l, int r) {
        return sum(r) - sum(l);
    }
};

signed main() {
    int n;
    cin >> n;
    
    vector<vector<int>> f(n+1);
    for (int i = 1; i <= n; i++) {
        cin >> c[i];
        f[c[i]].push_back(i);
    }
    for (int i = 1; i <= n; i++)     cin >> x[i];
    
    BIT<int> fen(n+1);
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        ans += fen.rangeSum(x[i] + 1, n+1);
        fen.add(x[i], 1);
    }
    
    //消除影响
    for (int i = 1; i <= n; i++) 
        fen.add(x[i], -1);
    
    for (int c = 1; c <= n; c++) {
        for (auto i : f[c]) {
            ans -= fen.rangeSum(x[i] + 1, n+1);
            fen.add(x[i], 1);
        }
        //消除影响
        for (auto i : f[c]) 
            fen.add(x[i], -1);
    }    
    cout << ans << endl;
}

//全部的逆序对数量-相同颜色的逆序对数量

G - Replace

题意

现有字符串s和t,给定k种转换规则,问利用这些规则,最少需要多少次就能实现s到t的转换

分析

其实还挺难想的,一个区间dp
g[i][l][r]: i字母出发,变为t[l,r)的代价
然后去更新dp数组,因为每次起点都是给定的l,所以可以利用滚动数组优化空间
然后从t的末尾开始往前读,若当前位置为l,则处理的是变为t[l,t.size())所需的代价
(具体的可以看代码注释)

Code

此代码来自伟大的师父
(我只是加了点注释)

#include<bits/stdc++.h>
#define int long long

using namespace std;
const int N = 55;

string s,t,a[N];
char c[N];
int k;
int g[30][N][N]; //dp[i][l][r]: i字母出发,变为t[l,r)的代价
int dp[2][N]; //滚动数组,都是从l开始变

signed main() {
	cin>>s>>t>>k;
	for(int i=0;i<k;i++) 
	    cin>>c[i]>>a[i], c[i] -= 'a';

	a[k]=s;
	c[k++]=26; //问题转化为:s->一个不存在的字符->t

	for(int c=0;c<27;c++) {
		for(int l=0;l<t.size();l++) {
			for(int r=l+1;r<=t.size();r++) //后缀段匹配的代价
                g[c][l][r]=1e9;
			if(t[l]==c+97) //就是自身,即t[l,l)处为c
                g[c][l][l+1]=0; //[l,l+1)
		}
	}

	for(int l=t.size();l--;) {
        //后缀匹配t,以l为起点往后匹配,起点不断左移,直至匹配完一整个串
        while(1){ //不停找到最小代价
            bool ch=0;
            for(int i=0;i<k;i++){ //枚举每一种转换规则
                int now=0; //滚动数组
                for(int j=l;j<=t.size();j++) //[j, l)
                    dp[now][j]=1e9; 
                dp[now][l]=1; //[l,l)

                for(char c:a[i]){
                    int nxt=1-now;//滚
                    for(int ii=l;ii<=t.size();ii++)
                        dp[nxt][ii]=1e9; //初始化代价为一个很大的值
                    for(int ii=l;ii<t.size();ii++)
                        for(int j=ii+1;j<=t.size();j++)
                            dp[nxt][j]=min(dp[nxt][j],dp[now][ii]+g[c-97][ii][j]); 
                            //区间dp,相当于[l,ii),[ii,j) -> [l,j)
                    now=nxt;
                }

                for(int r=l+1;r<=t.size();r++) { //更新以c[i]为起点,变为t[l]~t[r-1]这一连续段所需代价
                    if(g[c[i]][l][r]>dp[now][r]){ //dp[now][r]默认起点为l,故为变成t[l,r)的代价
                        ch=1;
                        g[c[i]][l][r]=dp[now][r];
                    }
                }
            }
            if(ch==0) break; //没有更新,索命已经是最小代价了
        }
    }
	int ans=g[26][0][t.size()];//从s出发,匹配t[0,t.size())所需代价
	cout<<ans%(int)1e9-1<<endl;
	return 0;
}
posted @ 2022-07-25 22:37  Sakana~  阅读(262)  评论(4编辑  收藏  举报