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次,可能的结果为:
- value++, money+=x[i];
- 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:
- op == 1, x &= a;
- op == 2, x |= a;
- 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;
}