AtCoder Beginner Contest 443
总括
题解包含A-F题,赛时快速切掉了ABCD四道题(第一次做到D),E题一直使用浮点运算,有一个测试点精度不够,赛后学习使用极性排序,突然发现F更容易出来

A - Count .
思路
按照题意模拟即可
AC代码
void solve(){
string s;
int ans = 0;
cin >> s;
for(auto i : s){
if(i == 'i' || i == 'j')ans ++ ;
}
cout << ans << endl;
}
B - Music Player
思路
根据题意模拟
AC代码
void solve(){
cin >> n;
for(int i = 1; i <= n ; i ++ ){
cin >> op;
if(op == 1){
num ++ ;
}
else if(op == 2){
if(num >= 1) num -- ;
}
else {
if(key)key = false;
else key = true;
}
if(num >= 3 && key)cout << "Yes" << endl;
else cout << " No" << endl;
}
}
C - Peer Review
思路
先统计出每个人可以审稿的人数 \(m_i\) ,计算\(C_{m_i}^{3}\)
AC代码
void solve(){
cin >> n >> m;
for(int i = 1; i <= n ; i ++ ){
mp[i] = n - 1;
}
for(int i = 1; i <= m; i ++ ){
int a, b;
cin >> a >> b;
mp[a] --;
mp[b] --;
}
for(int i = 1; i <= n ; i ++ ){
int x = mp[i];
if(x < 3){
cout << 0 << " ";
}
else{
cout << (x * (x - 1) * (x - 2)) / 6 << " ";
}
}
cout << endl;
}
D - Swap and Range Sum
思路
考察前缀和,对于每次操作等价于:
\[s[i] = s[i] - a[i] + a[i + 1]
\]
然后交换 \(a[i]\) 和 \(a[i + 1]\)
AC代码
void solve(){
s[0] = 0;
cin >> n >> m;
for(int i = 1; i <= n ; i ++ ){
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
while(m -- ){
int op;
cin >> op;
if(op == 1){
int x;
cin >> x;
s[x] = s[x] - a[x] + a[x + 1];
swap(a[x], a[x + 1]);
}
else{
int l, r;
cin >> l >> r;
cout << s[r] - s[l - 1] << endl;
}
}
}
E - Laser Takahashi
思路
考察极性排序
AC代码
bool cmp(const P &a, const P &b){
int ah = (a.y < 0 || (a.y == 0 && a.x > 0));
int bh = (b.y < 0 || (b.y == 0 && b.x > 0));
if(ah != bh)return ah > bh;
else return a.y * b.x > b.y * a.x;
}
void solve(){
int n, q;
cin >> n >> q;
for(int i = 1; i <= n ; i ++ ){
cin >> p[i].x >> p[i].y;
}
for(int i = 1; i <= n ; i ++ ){
num[i] = i;
}
sort(num + 1, num + 1 + n, [&](int i, int j){return cmp(p[i], p[j]);});
for(int i = 1; i <= n ; i ++ ){
rev[num[i]] = i;
}
cout << endl;
l[1] = 1;
for(int i = 2; i <= n ; i ++ ){
if(cmp(p[num[i - 1]], p[num[i]]))l[i] = i;
else l[i] = l[i - 1];
}
r[n] = n;
for(int i = n - 1; i >= 1 ; i -- ){
if(cmp(p[num[i]], p[num[i + 1]]))r[i] = i;
else r[i] = r[i + 1];
}
while( q -- ){
int a, b;
cin >> a >> b;
a = l[rev[a]];
b = r[rev[b]];
if(a < b)cout << b - a + 1 << endl;
else cout << n - a + b + 1 << endl;
}
}
F - Diagonal Separation 2
思路
dp,我们考虑合法状态下每一行的白色格子的个数,不难发现每一行白色格子的数量应当是递减的,这类似于一个阶梯状
我们定义 \(dp[i][j]\) 表示来到第i行,本行最终状态下白色格子数量为 \(j\) 个,的最少操作数
状态转移:
\[min_{k=j}^n\left\{dp[i-1][k]\right\}+j-pre[i][j]+suf[i][j+1]
\]
其中, \(pre[i][j]\) 表示第 \(i\) 行从 \(1\) 到 \(j\) 位置白色的数量, \(suf[i][j]\) 表示第 \(i\) 行从\(j + 1\) 到 \(n\) 位置白色的数量
但是这样是不行的,因为如果要枚举 \(k\) ,时间复杂度就会变成 \(O(n^3)\),因此我们考虑维护一个变量来记录上一行的后缀最小值, \(j\) 从大到小枚举,即可实现 \(O(n^2)\)
AC代码

浙公网安备 33010602011771号