【做题笔记】板刷 CodeForces
CF1987D World is Mine#
第一想法是贪心的决策,考虑到是博弈论,每一轮决策肯定都是最优的。显然贪心做法假掉。
发现问题具有最优子结构与后效性,考虑 dp。
将
-
若当前回合的剩余决策数立马用掉,不消除答案贡献,即为
; -
若当前回合的剩余决策数留下,不消除答案贡献,即为
; -
若当前回合的剩余决策数用掉
来消除答案贡献,即为 ;
三种决策取最小即可。
由于博弈论的特殊性:双方在相同回合数时的总操作数相同,所以最后肯定不会剩余下决策数。所以答案为
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 1e17
using namespace std;
const int N = 5e3 + 10;
int T, n, a[N], b[N], f[N][N];
void solve() {
int ans = 0, num = 0;
cin >> n;
For(i,1,n) b[i] = 0;
For(i,0,n) For(j,0,n) f[i][j] = inf;
For(i,1,n) cin >> a[i], b[a[i]]++;
sort(a + 1, a + n + 1);
int tot = unique(a + 1, a + n + 1) - a - 1;
n = tot;
f[0][0] = 0;
For(i,1,n) {
For(j,0,i) {
if(j != 0) f[i][j] = min(f[i-1][j], f[i-1][j-1]) + 1;
else f[i][0] = f[i-1][0] + 1;
if(b[a[i]] + j <= i-1) f[i][j] = min(f[i][j], f[i-1][j + b[a[i]]]);
}
}
cout << f[n][0] << '\n';
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
时间复杂度
CF1986D Mathematical Problem#
考虑搜索,
先枚举相邻合并段,分析答案贡献的情况:先默认全加,发现答案上限为
设
因为乘法运算结果很大,所以当答案超过
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define mod 262
using namespace std;
const int N = 25;
char a[N];
int T, n, num[N], sum[N], dp[N], ans = 261, fuck_you_code_bug_tot;
void solve() {
cin >> n;
int len = n-1, cnt0 = 0;
ans = 261;
For(i,1,n) cin >> a[i];
For(i,1,n-1) {
memset(num, 0, sizeof num);
For(j,1,i-1) num[j] = a[j] - '0', sum[j] = sum[j-1] + num[j];
num[i] = (a[i] - '0') * 10 + (a[i+1] - '0');
sum[i] = sum[i-1] + num[i];
For(j,i+2,n) num[j-1] = a[j] - '0', sum[j-1] = sum[j-2] + num[j-1];
For(j,1,len) {
cnt0 += (num[j] == 0);
dp[j] = sum[j];
int mul = num[j];
FOR(k,j-1,0) {
dp[j] = min(dp[j], dp[k] + mul);
mul *= num[k];
if(dp[j] > 261 || mul > 261) {
break;
}
}
}
ans = min({ans, dp[len], (cnt0 > 0ll ? 0ll : 261ll)});
}
cout << ans << '\n';
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1983D Swap Dilemma#
可以观察到在操作数无限的情况下,大步换和两两换效果相同。可以考虑
发现
用树状数组算分别算逆序对比较奇偶性即可,时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 2e5 + 10
using namespace std;
const int N = 4e5 + 10;
int T, n, a[N], b[N], p[N], t[N];
int lb(int x) {
return x & -x;
}
int ask(int x) {
int ans = 0;
for (int i = x; i; i -= lb(i)) {
ans += t[i];
}
return ans;
}
void upd(int x, int k) {
for (int i = x; i <= inf; i += lb(i)) {
t[i] += k;
}
}
void solve() {
vector<int> v1, v2;
int ans1 = 0, ans2 = 0;
cin >> n;
For(i,1,n) cin >> a[i], p[i] = i, v1.push_back(a[i]);
For(i,1,n) cin >> b[i], v2.push_back(b[i]);
sort(v1.begin(), v1.end());
sort(v2.begin(), v2.end());
for (int i = 0; i < v1.size(); ++i) {
if(v1[i] != v2[i]) {
puts("NO");
return ;
}
}
For(i,1,n) {
ans1 += ask(a[i]);
upd(a[i], 1);
}
For(i,1,n) upd(a[i], -1);
For(i,1,n) {
ans2 += ask(b[i]);
upd(b[i], 1);
}
For(i,1,n) upd(b[i], -1);
if((ans1 & 1) == (ans2 & 1)) {
puts("YES");
} else {
puts("NO");
}
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1986E Beautiful Array#
考虑假设两数
定义“一组”的概念为
对于一组来说,相邻两项凑一起算贡献肯定最优,自然推出了组长为偶数的情况,便是两两配对贡献。
考虑奇数组的情况:先枚举中间点
分
-
当
为偶数时,若出现奇数组则无解,否则分组算贡献累加即可。 -
当
为奇数时,若出现两组以上的奇数组则无解,否则分组算贡献累加,奇数组单独处理。
总时间复杂度:
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 1e11
using namespace std;
const int N = 2e5 + 10;
int T, n, k, a[N];
void solve() {
int ans = 0;
cin >> n >> k;
For(i,1,n) cin >> a[i];
sort(a + 1, a + n + 1, [](int x, int y){return (x % k == y % k ? x < y : x % k < y % k);});
// For(i,1,n) cerr << a[i] << ' ';
// cerr << '\n';
if(n & 1) {
int r = 1, l = 1, sum = 0, f = 0, Min = inf;
vector<int> v;
int cnt = 1;
For(i,1,n) {
if(a[i] % k == a[i+1] % k && i < n) cnt++, v.push_back(a[i]);
else {
if(cnt & 1) {
v.push_back(a[i]);
f++; r = i, l = r - v.size() + 1;
}
else vector<int>().swap(v);
cnt = 1;
}
}
for (int i = 1; i <= l-1; i += 2) ans += a[i+1] - a[i];
for (int i = r+1; i <= n; i += 2) ans += a[i+1] - a[i];
For(i,l,r) sum += (((i-l+1) & 1) ? 1 : -1) * a[i];
Min = min(Min, sum - a[l]);
For(i,l+1,r) {
if(!((i-l+1) & 1)) {
sum -= a[i-1] * 2;
sum += a[i] * 2;
}
Min = min(Min, sum - a[i]);
}
if(f > 1) {
cout << "-1\n";
return ;
}
cout << (ans + Min) / k << '\n';
} else {
int cnt = 1;
For(i,1,n) {
if(a[i] % k == a[i+1] % k && i < n) {
cnt++;
}
else {
if(cnt & 1) {
cout << "-1\n";
return ;
}
cnt = 1;
}
}
for (int i = 1; i <= n; i += 2) {
ans += a[i+1] - a[i];
}
cout << ans / k << '\n';
}
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1986F Non-academic Problem#
考虑快速计算贡献:设将图分为大小为
由于要最小化路径树,且只能删去一条边。所以最好的情况是将图尽可能分为两部分,所以满足删除条件的边就是割边。
先缩边双联通分量,然后枚举割边(缩成树后跑 dfs)。然后计算删去该割边的贡献即可,最后取最小的贡献作为答案。
总时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 1e17
using namespace std;
const int N = 2e5 + 10;
struct Node {
int v, nx, u;
} e[N], E[N];
int T, n, m, h[N], H[N], dfn[N], low[N], cut[N], dcc[N], Col, tot = 1, idx, Idx = 1, siz[N];
void add(int u, int v) {
e[++tot] = (Node){v, h[u], u};
h[u] = tot;
}
void Add(int u, int v) {
E[++Idx] = (Node){v, H[u], u};
H[u] = Idx;
}
void tarjan(int x, int fa) {
dfn[x] = low[x] = ++idx;
for (int i = h[x]; i; i = e[i].nx) {
int y = e[i].v;
if(!dfn[y]) {
tarjan(y, x);
low[x] = min(low[x], low[y]);
if(low[y] > dfn[x]) cut[i] = cut[i ^ 1] = 1;
} else if(y != fa) {
low[x] = min(low[x], dfn[y]);
}
}
}
void dfs(int x, int cnt) {
dcc[x] = cnt;
siz[cnt]++;
for (int i = h[x]; i; i = e[i].nx) {
int y = e[i].v;
if(dcc[y] || cut[i]) continue;
dfs(y, cnt);
}
}
void Dfs(int x, int fa) {
for (int i = H[x]; i; i = E[i].nx) {
int y = E[i].v;
if(y == fa) continue;
Dfs(y, x);
siz[x] += siz[y];
}
}
void solve() {
int ans = inf;
Col = idx = 0;
Idx = tot = 1;
memset(cut, 0, sizeof cut);
For(i,1,n) {
h[i] = H[i] = 0;
dfn[i] = low[i] = dcc[i] = siz[i] = 0;
}
cin >> n >> m;
For(i,1,m) {
int u, v; cin >> u >> v;
add(u, v), add(v, u);
}
tarjan(1, 1);
For(i,1,n) {
if(!dcc[i]) dfs(i, ++Col);
}
For(i,1,n) {
for (int j = h[i]; j; j = e[j].nx) {
int y = e[j].v;
if(dcc[i] != dcc[y]) Add(dcc[i], dcc[y]);
}
}
Dfs(1, 0);
For(i,1,n) {
ans = min(ans, (n * n - n - (2 * siz[i] * (n - siz[i]))) / 2);
}
cout << ans << endl;
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1982D Beauty of the mountains#
单独考虑每一个山的贡献很难做,不妨以
我们发现每在
这就是一个
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 505;
int T, n, m, k, a[N][N], s[N][N], Ans;
bool b[N][N];
int ask(int x1, int y1, int x2, int y2) {
return s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1];
}
void solve() {
Ans = 0;
int sum = 0;
cin >> n >> m >> k;
memset(s, 0, sizeof s);
For(i,1,n) For(j,1,m) cin >> a[i][j];
For(i,1,n) {
For(j,1,m) {
char c; cin >> c;
b[i][j] = c - '0';
}
}
For(i,1,n) {
For(j,1,m) {
sum += (b[i][j] ? a[i][j] : -a[i][j]);
int F = (b[i][j] == 1 ? 1 : -1);
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + F;
}
}
For(i,1,n-k+1) {
For(j,1,m-k+1) {
int k1 = ask(i, j, i+k-1, j+k-1);
Ans = __gcd(Ans, k1);
}
}
if(sum == 0) {
puts("YES");
return ;
}
if(Ans == 0) {
puts("NO");
return ;
}
if(sum % Ans == 0) puts("YES");
else puts("NO");
return ;
}
signed main() {
// ios::sync_with_stdio(0);
// cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1974D Ingenuity-2#
相同的操作下,操作顺序对最终的位置无影响。
机器人的位置初始都为
此外注意到
我们不希望只有一个机器人操作,所以让 R 机器人进行一次抵消操作,后面的抵消操作全部分配给 H。
其他的操作平均、同等分配即可。若无法分则判断无解。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 2e5 + 10, M = 200;
int T, n, b[M];
char a[N], ans[N];
void solve() {
memset(b, 0, sizeof b);
memset(ans, 0, sizeof ans);
cin >> n >> (a + 1);
if(n & 1) {
cout << "NO\n";
return ;
}
For(i,1,n) b[a[i]]++;
int k1 = min(b['N'], b['S']), k2 = min(b['E'], b['W']);
b['N'] -= k1, b['S'] -= k1, b['E'] -= k2, b['W'] -= k2;
int K1 = k1;
bool f1, f2; f1 = f2 = 0;
for (int i = 1; i <= n && (k1 >= 0 || K1 >= 0); ++i) {
if(a[i] == 'N' && k1 > 0) {
if(f1) ans[i] = 'H';
else if(!f1) ans[i] = 'R', f1 = 1;
k1--;
}
if(a[i] == 'S' && K1 > 0) {
if(f2) ans[i] = 'H';
else if(!f2) ans[i] = 'R', f2 = 1;
K1--;
}
}
K1 = k2;
for (int i = 1; i <= n && (k2 >= 0 || K1 >= 0); ++i) {
if(a[i] == 'E' && k2 > 0) {
if(f1) ans[i] = 'H';
else if(!f1) ans[i] = 'R', f1 = 1;
k2--;
}
if(a[i] == 'W' && K1 > 0) {
if(f2) ans[i] = 'H';
else if(!f2) ans[i] = 'R', f2 = 1;
K1--;
}
}
bool f = 1;
for (int i : {'N', 'S', 'E', 'W'}) {
f &= (b[i] == 0 && n <= 2);
if(b[i] & 1) {
cout << "NO" << '\n';
return ;
}
}
if(f) {
cout << "NO" << '\n';
return ;
}
For(i,1,n) {
if((b[a[i]] & 1) && !ans[i]) ans[i] = 'R';
else if(!(b[a[i]] & 1) && !ans[i]) ans[i] = 'H';
b[a[i]]--;
}
For(i,1,n) cout << ans[i];
cout << '\n';
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1988D The Omnipotent Monster Killer#
第一眼有点像最大独立集,可以考虑往这方面想。
由于有了回合的限制,所以在 dp 时要多加一维来除去回合的后效性。设
转移也很显然,父亲和儿子不能在同一回合被选中,所以只需要在 dfs 中枚举所有回合,然后把所有不同回合的儿子状态取最小贡献,转移至父亲即可。
考虑回合数,因为每一次选点会将树拆成森林,然后每一个小树又会减半,继续分。所以可以证明回合数在
所以转移与计算贡献加上总时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 1e18
using namespace std;
const int N = 3e5 + 10;
struct Node {
int v, nx;
} e[N << 1];
int T, n, a[N], h[N], dp[N][25], tot, m, sum, ans = inf;
void add(int u, int v) {
e[++tot] = (Node){v, h[u]};
h[u] = tot;
}
void dfs(int x, int fa) {
for (int i = h[x]; i; i = e[i].nx) {
int y = e[i].v;
if(y == fa) continue;
dfs(y, x);
}
if(x == 1) return ;
For(i,1,m) {
int num = inf;
For(j,1,m) {
if(i == j) continue;
num = min(num, dp[x][j]);
}
dp[fa][i] += (num == inf ? 0 : num);
}
}
void solve() {
ans = inf; sum = tot = 0;
For(i,1,n) h[i] = 0;
cin >> n;
For(i,1,n) cin >> a[i], sum += a[i];
For(i,1,n-1) {
int u, v;
cin >> u >> v;
add(u, v), add(v, u);
}
m = (int)ceil(log2(n))+1;
For(i,1,n) For(j,1,m) dp[i][j] = a[i] * j;
dfs(1, 0);
For(i,1,m) ans = min(ans, dp[1][i]);
cout << ans << '\n';
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1980D GCD-sequence#
考虑在原数列中删去一个数
因为
先预处理出
然后暴力枚举删除哪个
总时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 1e8
using namespace std;
const int N = 2e5 + 10;
int T, n, a[N], b[N];
void solve() {
cin >> n;
For(i,1,n) cin >> a[i];
a[n+1] = a[0] = 0;
For(i,1,n-1) b[i] = __gcd(a[i], a[i+1]);
b[n] = inf;
int l = 1, r = n-1;
while(l <= n-1 && b[l+1] >= b[l]) l++;
while(r >= 1 && b[r-1] <= b[r]) r--;
if(l == n-2 || r == 2) {
puts("YES"); return ;
}
For(i,1,n) {
int New = __gcd(a[i-1], a[i+1]);
if(New >= b[max(0ll, i-2)] && New <= b[min(n, i+1)] && l >= i - 2 && r <= i + 1) {
puts("YES"); return ;
}
}
puts("NO");
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1401F Reverse and Swap#
操作
可以发现对于操作
记得操作
总时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 20, M = 1e7 + 10;
struct Node {
int ls, rs;
int sum;
} t[M];
int n, q, idx, root, rev[N];
void pushup(int p) {
t[p].sum = t[t[p].ls].sum + t[t[p].rs].sum;
}
void build(int &p, int l, int r) {
p = ++idx;
if(l == r) {
cin >> t[p].sum;
return ;
}
int mid = l + r >> 1;
build(t[p].ls, l, mid);
build(t[p].rs, mid + 1, r);
pushup(p);
}
void upd(int p, int l, int r, int x, int k, int level) {
if(l == r) {
t[p].sum = k;
return ;
}
int ls = t[p].ls, rs = t[p].rs;
if(rev[level]) swap(ls, rs);
int mid = l + r >> 1;
if(x <= mid) upd(ls, l, mid, x, k, level-1);
else upd(rs, mid + 1, r, x, k, level-1);
pushup(p);
}
int ask(int p, int l, int r, int L, int R, int level) {
if(L <= l && r <= R) {
return t[p].sum;
}
int ls = t[p].ls, rs = t[p].rs;
if(rev[level]) swap(ls, rs);
int mid = l + r >> 1, ans = 0;
if(L <= mid) ans += ask(ls, l, mid, L, R, level-1);
if(R > mid) ans += ask(rs, mid + 1, r, L, R, level-1);
return ans;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> q;
build(root, 1, (1<<n));
while(q--) {
int op; cin >> op;
if(op == 1) {
int x, k;
cin >> x >> k;
upd(root, 1, (1<<n), x, k, n);
} else if(op == 2) {
int k; cin >> k;
For(i,0,k) rev[i] ^= 1;
} else if(op == 3) {
int k; cin >> k;
rev[k+1] ^= 1;
} else {
int l, r; cin >> l >> r;
cout << ask(root, 1, (1<<n), l, r, n) << '\n';
}
}
return 0;
}
CF1322C Instant Noodles#
引理:在整数范围内,
证明:可以找到一个整数
因为有
所以
此时
有了这一点理论,我们发现本题可以看成是求
不妨将右部点看成左部点的映射,能观察到在右部点选择一个点等价于在左部点选择点集。贡献也能相应的算出。
由于右部点
最后只要注意合并相同的集合,并合并贡献即可。
集合用 set,映射用 map,总时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 1e6 + 10;
int T, n, m, ans, c[N];
set<int> e[N];
map<set<int>, int> mp;
void solve() {
ans = 0;
map<set<int>, int>().swap(mp);
For(i,1,n) set<int>().swap(e[i]);
cin >> n >> m;
For(i,1,n) cin >> c[i];
For(i,1,m) {
int u, v;
cin >> u >> v;
e[v].insert(u);
}
For(i,1,n) {
if(e[i].size()) mp[e[i]] += c[i];
}
for (auto x : mp) {
ans = __gcd(ans, x.second);
}
cout << ans << '\n';
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1416B Make Them Equal#
没有第一眼看出来解法。
先判断无解:由于每一次操作涉及
然后发现
转移的过程很简单,就是对于
而
发现最后操作数不会超过
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 1e5 + 10;
struct Ans {
int i, j, x;
};
int T, n, a[N];
void solve() {
vector<Ans> ans;
int sum = 0, cnt = 0;
cin >> n;
For(i,1,n) cin >> a[i], sum += a[i];
if(sum % n != 0) {
cout << "-1\n";
return ;
}
sum /= n;
For(i,2,n) {
if(a[i] % i == 0) {
ans.push_back((Ans){i, 1, a[i] / i});
continue;
}
int k = (int)(ceil(1.0 * a[i] / i) * i);
ans.push_back((Ans){1, i, k - a[i]});
ans.push_back((Ans){i, 1, k / i});
}
For(i,2,n) ans.push_back((Ans){1, i, sum});
cout << ans.size() << '\n';
for (int i = 0; i < ans.size(); ++i) cout << ans[i].i << ' ' << ans[i].j << ' ' << ans[i].x << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1996E Decode#
题目要求统计的是每一个
可以想到一个子串会在不同的
考虑如何快速统计合法字串
由于前缀和可能出现负数,所以桶用 map 替代时间复杂度
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define mod 1000000007
using namespace std;
const int N = 2e5 + 10;
int T, n, sum[N];
char s[N];
map<int, int> mp;
void solve() {
int ans = 0;
map<int, int>().swap(mp);
cin >> (s + 1);
n = strlen(s + 1);
For(i,1,n) sum[i] = sum[i-1] + (s[i] == '0' ? -1 : 1);
For(i,1,n) {
mp[sum[i]] = (mp[sum[i]] + (n-i+1) % mod) % mod;
}
int A = 0;
For(i,1,n) {
ans = (ans + (i * mp[A]) % mod) % mod;
mp[sum[i]] = (mp[sum[i]] - (n-i+1) % mod + mod) % mod;
if(s[i] == '0') A--;
else A++;
}
cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1993C Light Switches#
很神奇的一道题。
考虑直接枚举位置
还可以缩小一下答案枚举的范围,记
枚举
时间复杂度
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
using namespace std;
const int N = 2e5 + 10;
int T, n, k, a[N], ans;
void solve() {
cin >> n >> k;
For(i,1,n) cin >> a[i];
sort(a + 1, a + n + 1);
int l = a[n], r = a[n] + 2 * k - 1;
For(i,1,n) {
for (int j = 0; a[i] + k * j <= r && a[i] + (j + 1) * k - 1 >= l; j += 2) {
r = min(r, a[i] + (j + 1) * k - 1);
l = max(l, a[i] + k * j);
}
}
For(i,l,r) {
For(j,1,n) {
if(((i - a[j]) / k) % 2 != 0) {
goto yzy;
}
}
cout << i << '\n';
return ;
yzy:;
}
cout << "-1\n";
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1970E1 Trails (Easy)#
很暴力的计数题。
设
先考虑从
枚举回合
答案为
时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define mod 1000000007
using namespace std;
const int M = 105, N = 1005;
int n, m, s[M], l[M], dp[N][M];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> m >> n;
For(i,1,m) cin >> s[i];
For(i,1,m) cin >> l[i];
dp[0][1] = 1;
For(i,1,n) {
For(j,1,m) {
For(k,1,m) {
dp[i][j] += (dp[i-1][k] % mod * (max(0ll, (s[j] + l[j]) * (s[k] + l[k]) - (l[j] * l[k])) % mod)) % mod;
}
}
}
int ans = 0;
For(i,1,m) {
ans += dp[n][i];
ans %= mod;
}
cout << ans << '\n';
return 0;
}
CF1970E2 Trails (Medium)#
考虑到
我们令
发现式子有乘,有加,于是考虑构造矩阵进行加速。
对此矩阵进行快速幂(迭代
得到矩阵
答案便为
时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define mod 1000000007
using namespace std;
const int MM = 105, N = 1005;
int n, m, s[MM], l[MM], ans, r[MM];
struct Matrix {
int M[MM][MM];
void clean() {
For(i,1,m) For(j,1,m) M[i][j] = 0;
}
void init() {
For(i,1,m) M[i][i] = 1;
}
Matrix friend operator * (const Matrix a, const Matrix b) {
Matrix Ans; Ans.clean();
For(i,1,m) {
For(j,1,m) {
For(k,1,m) {
Ans.M[i][j] = (Ans.M[i][j] + (a.M[i][k] % mod * b.M[k][j] % mod) % mod) % mod;
}
}
}
return Ans;
}
};
Matrix qpow(Matrix a, int b) {
Matrix Ans;
Ans.init();
for (; b; b >>= 1) {
if(b & 1) Ans = Ans * a;
a = a * a;
}
return Ans;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> m >> n;
For(i,1,m) {cin >> s[i]; s[i] %= mod;}
For(i,1,m) {cin >> l[i]; r[i] = (s[i] + l[i] % mod) % mod;}
Matrix T, Ans;
Ans.clean();
Ans.M[1][1] = 1;
For(i,1,m) {
For(j,1,m) {
T.M[i][j] = max(0ll, (r[i] * r[j]) - (l[i] * l[j]));
}
}
Ans = qpow(T, n) * Ans;
For(i,1,m) (ans += Ans.M[i][1]) %= mod;
cout << ans << '\n';
return 0;
}
CF1970E3 Trails (Hard)#
以上仍然过不了
然后答案为
直接对矩阵
第一次先对
时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define mod 1000000007
using namespace std;
const int N = 1e5 + 10;
int n, m, s[N], l[N], r[N];
struct Matrix {
int M[3][3];
void clean() {
For(i,1,2) For(j,1,2) M[i][j] = 0;
}
void init() {
For(i,1,2) M[i][i] = 1;
}
void print() {
For(i,1,2) {
For(j,1,2) cout << M[i][j] << ' ';
cout << endl;
}
}
Matrix friend operator * (const Matrix a, const Matrix b) {
Matrix Ans;
Ans.clean();
For(i,1,2) {
For(j,1,2) {
For(k,1,2) {
Ans.M[i][j] = (Ans.M[i][j] + (a.M[i][k] % mod * b.M[k][j] % mod) % mod) % mod;
}
}
}
return Ans;
}
} Ans;
struct MATRIX1 {
int M[3][N];
void clean() {
For(i,1,2) For(j,1,m) M[i][j] = 0;
}
void print() {
For(i,1,2) {
For(j,1,m) cout << M[i][j] << ' ';
cout << endl;
}
}
} B;
struct MATRIX2 {
int M[N][3];
void clean() {
For(i,1,m) For(j,1,2) M[i][j] = 0;
}
void print() {
For(i,1,m) {
For(j,1,2) cout << M[i][j] << ' ';
cout << endl;
}
}
} A, Ans1, Ansend;
Matrix qpow(Matrix a, int b) {
Matrix res; res.clean(); res.init();
for (; b; a = a * a, b >>= 1) {
if(b) res = res * a;
}
return res;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> m >> n;
For(i,1,m) cin >> s[i], s[i] %= mod;
For(i,1,m) cin >> l[i], l[i] %= mod, r[i] = (s[i] + l[i]) % mod;
Ans.clean(); A.clean(); B.clean(); Ans1.clean(); Ansend.clean();
For(i,1,2) For(j,1,m) B.M[i][j] = (i == 1 ? r[j] : (-l[j] + mod) % mod);
For(i,1,m) For(j,1,2) A.M[i][j] = (j == 1 ? r[i] : l[i]);
For(i,1,2) {
For(j,1,2) {
For(k,1,m) {
Ans.M[i][j] = (Ans.M[i][j] + (B.M[i][k] % mod * A.M[k][j] % mod) % mod) % mod;
}
}
}
Ans = qpow(Ans, n-1);
For(i,1,m) {
For(j,1,2) {
For(k,1,2) {
Ans1.M[i][j] = (Ans1.M[i][j] + (A.M[i][k] % mod * Ans.M[k][j] % mod) % mod) % mod;
}
}
}
int ans = 0;
For(i,1,m) {
int j = 1;
For(k,1,2) {
ans = (ans + (Ans1.M[i][k] * B.M[k][j]) % mod) % mod;
}
}
cout << ans << '\n';
return 0;
}
CF1537D Deleting Divisors#
可以想到,能走到一个必败点的就是必胜点,这样可以写出来一个
然后考虑所有的
然后考虑所有的
然后
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 1e7 + 10;
int n, T;
void solve() {
cin >> n;
if(n & 1) {
cout << "Bob\n";
} else if((1ll << (int)(ceil(__lg(n)))) == n) {
if((int)(ceil(__lg(n))) & 1) cout << "Bob\n";
else cout << "Alice\n";
} else {
cout << "Alice\n";
}
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
CF1498D Bananas in a Microwave#
考虑暴力 dp。
设
考虑优化枚举
设
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 1e5 + 10;
int n, m, f[N], g[N];
signed main() {
// ios::sync_with_stdio(0);
// cin.tie(0), cout.tie(0);
cin >> n >> m;
memset(f, -1, sizeof f);
f[0] = 0;
For(i,1,n) {
int t, x, y;
cin >> t >> x >> y;
memset(g, 0, sizeof g);
double x0 = (1.0 * x) / 100000;
For(j,0,m) {
if(f[j] == -1 || g[j] == y) continue;
if(t == 1) {
int k = j + ceil(x0);
if(k <= m) if(f[k] == -1) f[k] = i, g[k] = g[j] + 1;
} else {
int k = ceil(j * (1.0 * x) / 100000);
if(j && k <= m) if(f[k] == -1) f[k] = i, g[k] = g[j] + 1;
}
}
}
For(i,1,m) cout << f[i] << ' ';
return 0;
}
CF1976D Invertible Bracket Sequences#
思维题。
考虑括号序列的合法要求。记 "(" 为
考虑翻转一段区间之后判断括号系列是否合法,设翻转区间为
考虑
枚举
时间复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 2e5 + 10, M = 23;
int T, n, sum[N], idx, root[N], st[N][M];
char s[N];
map<int, vector<int> > pos;
int qry(int l, int r) {
int k = __lg(r - l + 1);
return max(st[l][k], st[r-(1<<k)+1][k]);
}
int query(int l, int r, int x) {
return (upper_bound(pos[x].begin(), pos[x].end(), r) - lower_bound(pos[x].begin(), pos[x].end(), l));
}
void solve() {
cin >> s + 1;
n = strlen(s + 1);
map<int, vector<int> >().swap(pos);
For(i,1,n) {
sum[i] = sum[i-1] + (s[i] == '(' ? 1 : -1);
pos[sum[i]].push_back(i);
st[i][0] = sum[i];
}
for (int j = 1; (1 << j) <= n; ++j) {
for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
st[i][j] = max(st[i][j-1], st[i+(1<<(j-1))][j-1]);
}
}
int ans = 0;
For(i,1,n) {
int l = i, r = n, R = i-1;
while(l <= r) {
int mid = l + r >> 1;
if(qry(l, mid) <= 2 * sum[i-1]) {
R = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
ans += query(i, R, sum[i-1]);
}
cout << ans << '\n';
return ;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
作者:Daniel-yao
出处:https://www.cnblogs.com/Daniel-yao/p/18326784
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效