A
读题的时候有一些细节注意一下。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int l , r; cin >> l >> r;
if((l + r) == 0 || (l + r) == 2 ) {
cout << "Invalid\n";
} else if ((l + r) == 1) {
if(l == 1) {
cout << "Yes\n";
} else {
cout << "No\n";
}
}
return 0;
}
B
模拟
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int a[N][N];
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= i;j ++) {
cin >> a[i][j];
}
}
int now = a[1][1];
for(int i = 2; i <= n; i ++) {
if(now >= i) {
now = a[now][i];
} else {
now = a[i][now];
}
}
cout << now << '\n';
return 0;
}
C
可以先预处理出修改的顺序order。
先正着扫一遍,对于
s
[
i
]
>
t
[
i
]
s[i] > t[i]
s[i]>t[i]越靠前的修改会导致更小的字典序。
然后倒着扫一遍,对于
s
[
i
]
<
t
[
i
]
s[i] < t[i]
s[i]<t[i]的修改,越靠后的修改会导致更小的字典序。
然后按照预处理出的修改顺序去修改即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int a[N][N];
int main()
{
string s, t;
cin >> s >> t;
if(s == t) {
cout << 0 << '\n';
return 0;
} else {
int now = 0, cnt = 0;
vector<string>stk;
for(int i = 0; i < s.size(); i++) {
if(s[i] != t[i]) {
cnt ++ ;
}
}
cout << cnt << '\n';
vector<int>order(s.size() + 1);
for(int i = 1; i <= cnt; i ++) {
bool f = true;
for(int j = 0; j < s.size(); j ++ ) {
if(!order[j] && s[j] > t[j]) {
order[j] = i;
f = false;
break;
}
}
if(!f) continue;
for(int j = s.size() - 1; j >= 0; j -- ) {
if(!order[j] && s[j] < t[j]) {
order[j] = i;
break;
}
}
}
for(int i = 1; i <= cnt; i ++ ) {
for(int j = 0; j < s.size(); j ++ ) {
if(order[j] == i) {
s[j] = t[j];
cout << s << '\n';
}
}
}
}
return 0;
}
D
stl的应用。
用两个set数组维护每行每列存在的墙。,然后当墙存在时,直接在对应的行列set里面删除。
其他情况需要去分情况维护对应set。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 4e5 + 10, MOD = 998244353;
set<int>h[N], w[N];
int H, W, q;
int main()
{
cin >> H >> W >> q;
for(int i = 1; i <= H; i ++ ) {
for(int j = 1; j <= W; j ++ ) {
h[i].insert(j);
}
}
for(int i = 1; i <= W; i ++ ) {
for(int j = 1; j <= H; j ++ ) {
w[i].insert(j);
}
}
i64 ans = H * W;
while(q -- ) {
int x, y;
cin >> x >> y;
if(h[x].find(y) != h[x].end()) {
h[x].erase(y);
w[y].erase(x);
ans --;
continue;
}
auto it = h[x].lower_bound(y);
if(it != h[x].end()) {
w[*it].erase(x);
h[x].erase(*it);
ans --;
}
it = h[x].lower_bound(y);
if(it != h[x].begin()) {
it = prev(it);
w[*it].erase(x);
h[x].erase(*it);
ans --;
}
it = w[y].lower_bound(x);
if(it != w[y].end()) {
h[*it].erase(y);
w[y].erase(*it);
ans --;
}
it = w[y].lower_bound(x);
if(it != w[y].begin()) {
it = prev(it);
h[*it].erase(y);
w[y].erase(*it);
ans --;
}
}
cout << ans << '\n';
}
E
看到这种题目:脑子里面就应该想到不是
组合数学
组合数学
组合数学就是
计数类
d
p
计数类dp
计数类dp.
最开始我是用组合数学去做的,但是发现后面重复情况不太好做,然后用计数类dp可以解决这道问题。
有个比较显然的dp思路是
d
p
[
i
]
:
以
i
结尾,并且满足要求的方案数
dp[i]:以i结尾,并且满足要求的方案数
dp[i]:以i结尾,并且满足要求的方案数
于是有转移
d
p
[
i
]
+
=
d
p
[
j
]
,
j
<
i
,
s
[
i
]
−
k
!
=
s
[
j
−
1
]
dp[i] += dp[j], j < i , s[i] - k != s[j - 1]
dp[i]+=dp[j],j<i,s[i]−k!=s[j−1]
对于
!
=
!=
!=其实是不太好处理,不妨容斥,考虑用总的去减相等的。
这样需要记录前面所有的
d
p
[
i
]
dp[i]
dp[i],同时需要维护所有前缀和相同的
d
p
[
i
]
dp[i]
dp[i]的值。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 4e5 + 10, MOD = 998244353;
i64 a[N], f[N], n, k;
i64 s[N];
/*
* f[i] : 以i结尾满足条件的方案数
* i可以向j转移的条件是s[i] - s[j - 1] != k,所有这样的j,构成了f[i]
* for(int i = 1; i <= n; i ++ ) {
* for(int j = 1; j <= i; j ++ ) {
* if(s[i] - s[j-1] != k) {
* f[i] = (f[i] + f[j]) % MOD;
* }
* }
* }
* 可以观察第二重循环,是对所有的sum(f[j])(j < i)减去sum(f[j]) (j < i && s[i] - s[j - 1] == k)
* 于是可以用一个map记录前面s[j]的所有dp[j]的和即mp[s[j]],这样转移的时候就可直接O(1)转移
*/
int main()
{
cin >> n >> k;
map<i64, i64> mp;
for(int i = 1; i <= n; i ++ ) {
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
mp[0] = 1;
f[0] = 1;
i64 sum = 1;//维护的是f[j] j < i的和
for(int i = 1; i <= n; i ++ ) {
f[i] = (sum - mp[s[i] - k] % MOD + MOD) % MOD;
(mp[s[i]] += f[i]) %= MOD;
(sum += f[i]) %= MOD;
}
cout << f[n] << '\n';
}