| A | B | C | D | E | F | |
| AGC043 | O | O | ||||
| AGC044 | ||||||
| AGC045 | O | |||||
| AGC046 | ||||||
| AGC047 | ||||||
| AGC048 | O | |||||
| AGC049 | O | O | ||||
| AGC050 | O | O | O | |||
| AGC051 | O | |||||
| AGC052 | O | |||||
| AGC053 | O |
AGC043
A - Range Flip Find Route
对于一个路径来说,连续的#段数就是这条路径的最小操作次数。算这个东西就行了。
#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
首先转为考虑问题,然后进行分类讨论。
- 如果序列中存在
,答案是
或
。那么序列的
等价,都可以认为是
,
是改变答案奇偶性的关键。
- 如果序列中不存在
,答案是
或
。那么可以认为
是
,
是改变答案奇偶性的关键。
那么查看在序列中的位置,从而判断出该位置到达终点位置的路径的奇偶性即可。
#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
需要想到这个题本质是对于当前的,是否可以用后面的
将它的影响消除。
倒序考虑。对的后缀做线性基。如果当前是
且对应的
不能插入线性基,则有必胜策略。
#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
这道题比较中国风,只要固定前缀模拟一下即可。注意前缀可以是。
#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
第一反应是缩点然后在上
,但是想了想觉得代码太长了,他们
分钟就过了,而且
不太可能考这种题。
但是没想到什么简单做法。看题解才明白要把期望拆开,转成每个点的期望次数求和。
设表示可以到达点
的点数,答案是
。
#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
转化为判定前缀异或和是否相同。这个操作就是当时进行赋值
操作。从左往右扫一遍即可。
#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
考虑重新编号。结论是编号
指向
。
因为在
步后可以走到
,其中
。
#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
考虑表示在只在
这段区间操作的情况下的最大权值和。
有如下种情况。
容易发现这三个
中间有
的倍数个
左端点右移
直接按照区间
的套路划分区间。
#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
题目问成功的方案数。正难则反,考虑失败的方案数。
容易发现对于一个序列中的连续段,这些段长度分别是
,失败需要满足
。
表示
这段区间出现
段
的字符串种类数。
转移有那么一丢丢麻烦,虽然很短。
#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
点集。
其中是平行于
观测方向的长度随机的向量。
分别至多看到
的点集。
题解说的这个随机比较神,我理解是为了使得的期望是
的
倍。
我并没有随机长度,而是把长度设置的分隔比较大,也能达到同样的效果。
#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
依次输出,
个
,
个
即可。
出现的
个
下标分别是
。因为
是
的循环节,所以
两者之间有
个
。
#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
有一个结论:前轮后手选择的
个数位于
。
最小化后手选择的数的和即可。
#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 ;
}
1万+

被折叠的 条评论
为什么被折叠?



