当前位置:网站首页>AtCoder abc256全题解(区间合并模板、矩阵快速幂优化dp、线段树……)
AtCoder abc256全题解(区间合并模板、矩阵快速幂优化dp、线段树……)
2022-06-22 20:34:00 【hans774882968】
传送门
作者:hans774882968以及hans774882968
A
水,略。
B
模拟即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 5 + 5;
int n, a[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {
}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
read (n);
int ans = 0;
rep (_, 1, n) {
int x;
read (x);
a[0]++;
dwn (i, 3, 0) {
if (i + x < 4) a[i + x] += a[i];
else ans += a[i];
a[i] = 0;
}
}
printf ("%d\n", ans);
return 0;
}
C-枚举
题意:有一个九宫格,每格填一个正整数,输入3 <= h[0~2] <= 30, 3 <= w[0~2] <= 30分别表示期望的每行和每列的数字的和。求方案数。
只需要枚举4个格子就能确定所有的数了,计算量30^4完全可行。我选择的是(0,0), (0,1), (1,0), (1,1)这4个格子。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e5 + 5;
int n, w[5], h[5];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {
}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
n = 3;
re_ (i, 0, n) read (w[i]);
re_ (i, 0, n) read (h[i]);
int ans = 0;
re_ (i0, 1, min (w[0], h[0]) - 1) {
re_ (i1, 1, min (w[1], h[0]) - 1) {
re_ (i3, 1, min (w[0], h[1]) - 1) {
re_ (i4, 1, min (w[1], h[1]) - 1) {
int i2 = h[0] - i0 - i1, i5 = h[1] - i3 - i4, i6 = w[0] - i0 - i3, i7 = w[1] - i1 - i4;
if (i2 > 0 && i5 > 0 && i6 > 0 && i7 > 0) {
int i81 = h[2] - i6 - i7, i82 = w[2] - i2 - i5;
if (i81 == i82 && i81 > 0) ++ans;
}
}
}
}
}
printf ("%d\n", ans);
return 0;
}
D-区间合并模板
区间合并模板,参考:https://blog.nowcoder.net/n/834a656e47df44e58e830fdd87d3e253。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e5 + 5;
int n;
pii a[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {
}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int main() {
read (n);
rep (i, 1, n) read (a[i].first, a[i].second);
sort (a + 1, a + n + 1);
vector<pii> ans;
for (int i = 1; i <= n; ++i) {
int l = a[i].first, r = a[i].second;
while (i <= n && a[i + 1].first <= r) r = max (r, a[++i].second);
ans.push_back ({
l, r});
}
for (pii v : ans) printf ("%d %d\n", v.first, v.second);
return 0;
}
E-图论建模,函数图的性质
题意
n <= 2e5个人排队拿糖果。输入长为n的两个数组x, c,表示如果i号人排在x[i]号人后面,则i号人会受到c[i]的伤害。请你求一个排列,使所有人受到的总伤害最小。保证i ≠ x[i]。
思路
尝试往逆序对、二分、贪心和自定义排序等方面想,一无所获。只好看答案,没想到是图论题!
这个图有n条有向边,i -> x[i],官方题解指出这种图叫做functional graph,顾名思义,以i为自变量,x[i]为因变量,确实符合函数定义。函数图有如下特殊性质:它的每个无向图意义下的连通分量恰好有一个环。官方题解用数学归纳法,用了不少篇幅来证明。其实感性上更好理解:每个点都必须有一个出度,而DAG拓扑序最大的点出度为0,故必定有环。而环套环要求有些点出度至少为2。
这样每个连通分量就分为非环的部分和环上的部分。对于非环的部分x1 -> x2 -> ...,直接定顺序为x1 -> x2 -> ...伤害为0,最优。环上的部分至少有一个人会受伤害。那么我们只让伤害值最小的人受伤即可,贡献min(c[y1],c[y2],...)。
代码
uf[]标记无向图意义下的连通分量是否被访问过。简单的并查集。- 为了得到环上的所有点:
vis[]是中间变量,u从i开始,跳到第一次vis[u] = true的时候,u是环上的一点,接着跳就能得到环上的所有点了。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 2e5 + 5;
int n, c[N], a[N], fa[N];
bool vis[N], uf[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {
}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int find (int x) {
return x == fa[x] ? x : fa[x] = find (fa[x]);
}
int main() {
read (n);
re_ (i, 0, n) read (c[i]), c[i]--;
re_ (i, 0, n) read (a[i]);
re_ (i, 0, n) fa[i] = i;
re_ (i, 0, n) {
int f1 = find (i), f2 = find (c[i]);
fa[f1] = f2;
}
LL ans = 0;
re_ (i, 0, n) {
int fi = find (i);
if (uf[fi]) continue;
uf[fi] = true;
vis[i] = true;
int u = c[i];
while (!vis[u]) {
vis[u] = true;
u = c[u];
}
int mn = a[u];
for (int v = c[u]; v != u; v = c[v])
mn = min (mn, a[v]);
ans += mn;
}
printf ("%lld\n", ans);
return 0;
}
F-树状数组
题意
给你长为n <= 2e5的数组。有两种操作:
- 单点修改。
- 求3阶前缀和
D[x](见原题,1-indexed),模998244353。
思路
套路题。首先考虑把它写成只与a[1], a[2], ..., a[x]有关的形式。对于二阶前缀和,用贡献思想很容易发现是C[x] = x*a[1]+(x-1)*a[2]+...+1*a[x],所以D[x] = (x+2-1)*(x+1-1)/2*a[1]+(x+2-2)*(x+1-2)/2*a[1]+...+(x+2-x)*(x+1-x)/2*a[x]。
对于(x+2-i)*(x+1-i)/2*a[i],我们希望x的函数与和i有关的函数分离,则x的函数是系数,和i有关的函数是需要维护的数组,这样题目就做完了。分离出(x+2)*(x+1)/2 * a[i] - (2*x+3)/2 * (i*a[i]) + 1/2 * (i*i*a[i])。因此只需要维护a[i], i*a[i], i*i*a[i]的前缀和。
拓展:k阶前缀和同理可做。但k较大的时候是否能写代码来求多项式系数呢?我暂时做不到。
代码
- 注意一切皆可模法。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
#define lowbit(x) ((x) & (-(x)))
const int N = 2e5 + 5;
const int mod = 998244353;
const int I2 = 499122177;
int n, q, a[N];
LL c0[N], c1[N], c2[N];
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {
}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
void mdy (LL C[], int x, LL v) {
for (; x <= n; x += lowbit (x) ) C[x] = (C[x] + v) % mod;
}
LL qry (LL C[], int x) {
LL ret = 0;
for (; x; x -= lowbit (x) ) ret = (ret + C[x]) % mod;
return ret;
}
int main() {
read (n, q);
rep (i, 1, n) read (a[i]), a[i] %= mod;
rep (i, 1, n) {
mdy (c0, i, a[i]);
mdy (c1, i, 1LL * i * a[i] % mod);
mdy (c2, i, 1LL * i * i % mod * a[i] % mod);
}
while (q--) {
int op;
read (op);
if (op == 1) {
int x;
LL v;
read (x, v);
v %= mod;
mdy (c0, x, (v - a[x] + mod) % mod);
mdy (c1, x, x * (v - a[x] + mod) % mod);
mdy (c2, x, 1LL * x * x % mod * (v - a[x] + mod) % mod);
a[x] = v;
} else {
LL x;
read (x);
LL k0 = (x + 2) * (x + 1) % mod * I2 % mod, k1 = (2 * x + 3) * I2 % mod;
LL ans = (k0 * qry (c0, x) % mod - k1 * qry (c1, x) % mod + I2 * qry (c2, x) % mod + mod) % mod;
printf ("%lld\n", ans);
}
}
return 0;
}
G-矩阵快速幂优化dp
太难了,只能想到2^D的做法(枚举顶点颜色的状态位),直接学习题解了……
定义dp[i][S = 0~3]为填了前i条边,第一个顶点和当前的边的顶点的颜色分别为状态位的高位和低位的方案数,1表示白色(1表示黑色同理可做)。显然第n条边的顶点和第一个顶点颜色要相同,所以答案dp[n][0] + dp[n][3]。
但我们发现没法转移,因为不知道之前的白色有几种,没法保证白色点数相同。那白色点数相同的保证就用外部限制来实现,所以我们先枚举所需颜色种类数k = 0~D+1。转移:dp[i][S] = sum(C(D - 1, k - bc[S1]) * dp[i-1][S0]),这里S和S0的高位要相同,而S1 = (S % 2) << 1 | (S0 % 2)表示上一条边的顶点和当前边的顶点颜色组成的状态位,bc[]表示白色点的个数。边界:dp[1][S] = C(D - 1, k - bc[S])。
转移只与i-1有关,可矩阵快速幂优化。
总结:想法要大胆,看到n较大也不一定就放弃思考dp做法了,因为还可以用矩阵快速幂来保证复杂度。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 1e4 + 5;
const int mod = 998244353;
LL pw, fac[N], ifac[N];
int D, n = 4;
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {
}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
struct Mat {
LL m[5][5];
Mat() {
memset (m, 0, sizeof m);
}
};
Mat mul (Mat a, Mat b) {
Mat ret;
re_ (i, 0, n) {
re_ (j, 0, n) {
LL s = 0;
re_ (k, 0, n) s = (s + a.m[i][k] * b.m[k][j] % mod) % mod;
ret.m[i][j] = s;
}
}
return ret;
}
Mat q_pow (Mat a, LL b) {
Mat ret;
re_ (i, 0, n) ret.m[i][i] = 1;
for (; b; b >>= 1) {
if (b & 1) ret = mul (ret, a);
a = mul (a, a);
}
return ret;
}
LL q_pow (LL a, LL b) {
LL ret = 1;
for (; b; b >>= 1) {
if (b & 1) ret = ret * a % mod;
a = a * a % mod;
}
return ret;
}
void init_C (int n) {
fac[0] = 1;
rep (i, 1, n) fac[i] = fac[i - 1] * i % mod;
ifac[n] = q_pow (fac[n], mod - 2);
dwn (i, n - 1, 0) ifac[i] = (i + 1) * ifac[i + 1] % mod;
}
LL C (int x, int y) {
if (y < 0 || x < y) return 0;
return fac[x] * ifac[y] % mod * ifac[x - y] % mod;
}
int main() {
init_C (N - 5);
read (pw, D);
LL ans = 0;
rep (k, 0, D + 1) {
Mat m0, bas;
re_ (S, 0, n) {
re_ (S0, 0, n) {
if (S / 2 != S0 / 2) continue;
int u = (S % 2) << 1 | (S0 % 2);
m0.m[S][S0] = C (D - 1, k - __builtin_popcount (u) );
}
}
re_ (S, 0, n) bas.m[S][0] = C (D - 1, k - __builtin_popcount (S) );
Mat m1 = mul (q_pow (m0, pw - 1), bas);
ans = (ans + m1.m[0][0] + m1.m[3][0]) % mod;
}
printf ("%lld\n", ans);
return 0;
}
H-线段树
思路
即ex题。官方题解的两种做法都太难了,但是操作1的区间向下整除会导致数字快速减小,而操作2的区间覆盖会导致整个数组的丰富程度下降,所以我们会想尝试加一个tag,表示区间的数字是否完全相同,在区间数字完全相同时停止往下遍历。
加这个tag的思路的主要来源是这道线段树模板题:花神游历各国,hdu上有一道题,区间求欧拉函数+求区间和,也是一样的思路。
加这个tag的思路很容易理解,也能AC,但我不清楚它在这题的复杂度对不对。
实现
- 结构体需要3个属性:
v,ctg,s。v = -1表示该区间的值不是完全相同,v >= 0表示值完全相同;ctg表示区间覆盖的懒标记;s表示区间和。 - 我选择了先build,再对数组进行
n次单点覆盖,这就要求把叶节点的值设为0,而非叶节点的ctg设为-1。也可以直接读入数组再build。 s不是可选的而是必须的,把s去掉则查区间和的时候会走得更深,导致TLE。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
#define ls(x) ((x) << 1)
#define rs(x) ((x) << 1 | 1)
const int N = 5e5 + 5;
int n, q;
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {
}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
struct Node {
int v, ctg;
LL s;
} nd[N << 2];
void update (int x, int v, int l, int r) {
nd[x].ctg = nd[x].v = v;
nd[x].s = 1LL * v * (r - l + 1);
}
void pushup (int x) {
nd[x].v = nd[ls (x)].v == nd[rs (x)].v ? nd[ls (x)].v : -1;
nd[x].s = nd[ls (x)].s + nd[rs (x)].s;
}
void pushdown (int x, int l, int r) {
if (!~nd[x].ctg) return;
int v = nd[x].ctg, mid = (l + r) >> 1;
nd[ls (x)].ctg = nd[rs (x)].ctg = v;
nd[ls (x)].v = nd[rs (x)].v = v;
nd[ls (x)].s = (mid - l + 1LL) * v;
nd[rs (x)].s = 1LL * (r - mid) * v;
nd[x].ctg = -1;
}
void build (int x, int l, int r) {
nd[x].ctg = -1;
if (l == r) {
update (x, 0, l, r);
return;
}
int mid = (l + r) >> 1;
build (ls (x), l, mid);
build (rs (x), mid + 1, r);
pushup (x);
}
void idiv (int ql, int qr, int v, int x = 1, int l = 1, int r = n) {
if (ql <= l && r <= qr && ~nd[x].v) {
update (x, nd[x].v / v, l, r);
return;
}
pushdown (x, l, r);
int mid = (l + r) >> 1;
if (ql <= mid) idiv (ql, qr, v, ls (x), l, mid);
if (qr > mid) idiv (ql, qr, v, rs (x), mid + 1, r);
pushup (x);
}
void cover (int ql, int qr, int v, int x = 1, int l = 1, int r = n) {
if (ql <= l && r <= qr && ~nd[x].v) {
update (x, v, l, r);
return;
}
pushdown (x, l, r);
int mid = (l + r) >> 1;
if (ql <= mid) cover (ql, qr, v, ls (x), l, mid);
if (qr > mid) cover (ql, qr, v, rs (x), mid + 1, r);
pushup (x);
}
LL qry (int ql, int qr, int x = 1, int l = 1, int r = n) {
if (ql <= l && r <= qr) return nd[x].s;
pushdown (x, l, r);
int mid = (l + r) >> 1;
LL ans = 0;
if (ql <= mid) ans += qry (ql, qr, ls (x), l, mid);
if (qr > mid) ans += qry (ql, qr, rs (x), mid + 1, r);
pushup (x);
return ans;
}
int main() {
read (n, q);
build (1, 1, n);
rep (i, 1, n) {
int v;
read (v);
cover (i, i, v);
}
while (q--) {
int op, l, r, v;
read (op, l, r);
if (op == 1) {
read (v);
idiv (l, r, v);
} else if (op == 2) {
read (v);
cover (l, r, v);
} else {
printf ("%lld\n", qry (l, r) );
}
}
return 0;
}
边栏推荐
- June25,2022 PMP Exam clearance manual-6
- Kdd'22 | Ali: fine tuning CTR estimation based on EE exploration
- Microsoft edge browser will support network speed measurement, built-in calculator and unit conversion tool
- 【MAVROS】MAVROS 啓動指南
- KDD'22 | 阿里: 基于EE探索的精排CTR预估
- Makefile:1860: recipe for target ‘cmake_ check_ build_ system‘ failed make: *** [cmake_check_build_syst
- [GWCTF 2019]mypassword XSS
- IDC发布中国数据治理报告 亿信华辰第一
- Vs code one key sorting shortcut
- Lesson 031: permanent storage: pickle a jar of delicious pickles | after class test questions and answers
猜你喜欢

勒索病毒横行下设备该如何进行加密防护

考生必读篇 | PMP6月25日考试临近,需要注意什么?

Ten thousand words long text | use RBAC to restrict access to kubernetes resources
![[GWCTF 2019]mypassword XSS](/img/26/3611fd5aae21ea004dcfcc2c623328.png)
[GWCTF 2019]mypassword XSS

Lesson 030: file system: introduce a big thing | after class test questions and answers

Lesson 033: exception handling: you can't always be right 2 | after class test questions and answers
![[icml2022] using virtual nodes to promote graph structure learning](/img/cc/8d009e3c073b4eeef090ac393b8199.png)
[icml2022] using virtual nodes to promote graph structure learning

Self service library system Tkinter interface and openpyxl form comprehensive design case
![[Li mu] how to read papers [intensive reading of papers]](/img/86/4894bdef31d47d3f9bf3206b997eed.jpg)
[Li mu] how to read papers [intensive reading of papers]

Optimization solver | gurobi's Mvar class: a sharp tool for matrix modeling and an alternative solution to dual problems (with detailed cases and codes attached)
随机推荐
微软 Edge 浏览器将支持网络测速,内置计算器和单位转换工具
The interception of Chinese and English strings in Oracle database is different
[recommended by Zhihu knowledge master] castle in UAV - focusing on the application of UAV in different technical fields
7-9 super Mary
Analysis of fegin
Dynamic tree + data table + pagination of spa project development
[GWCTF 2019]mypassword XSS
大不列颠泰迪熊加入PUBG 手游
Home page navigation + left menu of spa project development
自助图书馆系统-Tkinter界面和openpyxl表格综合设计案例
A hundred lines of code to realize reliable delay queue based on redis
为什么感觉中国人月薪过万已经很普遍了?
[interpretation of the paper] sort out the papers on the vision based autonomous landing platform of UAV
IDC publie le rapport sur la gouvernance des données en Chine
[ongoing update...] 2021 National Electronic Design Competition for college students (III) interpretation of the anonymous four axis space developer flight control system design
v-if和v-for哪个优先级更高?
【知乎知识主推荐】 无人机中的城堡- 专注于无人机在不同技术领域的应用
Lesson 023 and 024: recursion: these little bunnies, Hanoi Tower after class test questions and answers
How to use the data dictionary function in the low code platform of the Internet of things?
Optimization solver | gurobi's Mvar class: a sharp tool for matrix modeling and an alternative solution to dual problems (with detailed cases and codes attached)