CF
1388D - Captain Flint and Treasure
- 因为每一个 i i i 都会对 b i b_i bi 产生影响,这种影响比较复杂,所以考虑建图,从 i i i 到 b i b_i bi 连边
- 不会受到影响的即为入度为 0 的点,从这些点开始拓扑排序
- 如果当前点 a i a_i ai 大于 0,直接累加进答案,如果小于 0,由于路径上的 a a a 会相互累加,所以不能直接加,将其存入栈中,倒序输出(消除累加性带来的影响)
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 1e5 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
struct cmp
{
int a, b, idx; // 存储的信息
bool operator> (const cmp &tmp) const
{
return b > tmp.b; // 制定自己的排序规则
}
};
void solve()
{
int n; cin >> n;
vector<int> a(n + 1), ind(n + 1);
for (int i = 1; i <= n; i ++ ) cin >> a[i];
vector<vector<int>> g(n + 1);
for (int i = 1; i <= n; i ++ )
{
int x; cin >> x;
if (x != -1)
{
g[i].push_back(x);
ind[x] ++ ;
}
}
queue<int> q;
stack<int> stk;
for (int i = 1; i <= n; i ++ )
{
if (ind[i] == 0) q.push(i);
}
int res = 0;
vector<int> ans;
while (q.size())
{
auto t = q.front();
q.pop();
if (a[t] >= 0)
{
res += a[t];
ans.push_back(t);
}
else stk.push(t);
for (int i = 0; i < g[t].size(); i ++ )
{
int j = g[t][i];
if (a[t] >= 0) a[j] += a[t];
ind[j] -- ;
if (ind[j] == 0) q.push(j);
}
}
while (stk.size())
{
auto t = stk.top();
stk.pop();
res += a[t];
ans.push_back(t);
}
cout << res << '\n';
for (auto t : ans) cout << t << ' ';
cout << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
}
383C - Propagating tree
- 首先想到需要区间修改单点查询,所以用线段树
- 要在线段树上处理一棵树,又想到将树转化成dfs序处理
- 这是没想到的地方:因为对于层数是奇数还是偶数,处理方式不同,所以建两颗线段树分别处理奇偶
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
const int N = 2e5 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
struct Node {
int l, r, sum, add;
} tr[2][N * 4];
// u表示当前树中结点编号 lr表示树中结点左右子结点
void pushup(Node &u, Node &l, Node &r)
{
/* 此处用[l]和[r]的值更新[u] */
u.l = l.l, u.r = r.r;
u.sum = l.sum + r.sum;
}
void pushup(int u)
{
pushup(tr[0][u], tr[0][u << 1], tr[0][u << 1 | 1]);
pushup(tr[1][u], tr[1][u << 1], tr[1][u << 1 | 1]);
}
void pushdown(int u)
{
for (int i = 0; i < 2; i ++ )
{
auto &root = tr[i][u], &left = tr[i][u << 1], &right = tr[i][u << 1 | 1];
if (root.add) // 当前结点有懒标记 向下传递
{
left.add += root.add, left.sum += (i64)(left.r - left.l + 1) * root.add;
right.add += root.add, right.sum += (i64)(right.r - right.l + 1) * root.add;
root.add = 0;
}
}
}
// u表示当前树中结点编号 lr表示区间左右端点
void build(int u, int l, int r)
{
if (l == r) // 左右端点相同表示到达叶子结点
{
tr[0][u] = {l, r}; // 创建该结点
tr[1][u] = {l, r};
}
else
{
tr[0][u].l = l, tr[0][u].r = r;
int mid = l + r >> 1; // 取中间值
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); // 分别构造左右两棵子树
pushup(u); // 利用pushup更新该点
}
}
void modify(int idx, int u, int l, int r, int d)
{
if (tr[idx][u].l >= l && tr[idx][u].r <= r) // 当前树中结点在所求区间之内
{
tr[idx][u].sum += (i64)(tr[idx][u].r - tr[idx][u].l + 1) * d; // 更新区间信息
tr[idx][u].add += d; // 打上懒标记
}
else // 当前树中结点不在所求区间之内
{
pushdown(u); // 将懒标记向下传递
int mid = tr[idx][u].l + tr[idx][u].r >> 1;
if (l <= mid) modify(idx, u << 1, l, r, d); // 与左半段有重合部分就更新左半段
if (r > mid) modify(idx, u << 1 | 1, l, r, d); // 与右半段有重合部分就更新右半段
pushup(u); // 由于modify修改了区间结点的信息,所以被修改的结点的祖先结点都需要重算一遍
}
}
Node query(int idx, int u, int l, int r)
{
if (tr[idx][u].l >= l && tr[idx][u].r <= r) return tr[idx][u]; // 当前区间在被查询区间之内 直接返回
else
{
pushdown(u);
int mid = tr[idx][u].l + tr[idx][u].r >> 1; // 取中间值
if (r <= mid) return query(idx, u << 1, l, r); // 被查询区间在当前区间左半部分
else if (l > mid) return query(idx, u << 1 | 1, l, r); // 被查询区间在当前区间右半部分
else // 被查询区间横跨当前区间的左右两部分
{
auto left = query(idx, u << 1, l, r); // 计算出左半部分值
auto right = query(idx, u << 1 | 1, l, r); // 计算出右半部分值
Node res;
pushup(res, left, right); // 更新结果
return res;
}
}
}
void solve()
{
int n, m;
cin >> n >> m;
vector<vector<int>> g(n + 1);
vector<int> a(n + 1), lt(n + 1), rt(n + 1), dep(n + 1);
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = 0; i < n - 1; i ++ )
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
build(1, 1, n);
int timestamp = 0;
function<void(int, int, int)> dfs = [&](int u, int fa, int d)
{
lt[u] = ++ timestamp;
dep[u] = d;
for (int i = 0; i < g[u].size(); i ++ )
{
int j = g[u][i];
if (j == fa) continue;
dfs(j, u, d + 1);
}
rt[u] = timestamp;
};
dfs(1, -1, 1);
while (m -- )
{
int op, idx, val;
cin >> op >> idx;
if (op == 1)
{
cin >> val;
modify(dep[idx] & 1, 1, lt[idx], rt[idx], val);
modify(!(dep[idx] & 1), 1, lt[idx], rt[idx], -val);
}
else
{
auto res = query(dep[idx] & 1, 1, lt[idx], lt[idx]);
cout << res.sum + a[idx] << '\n';
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
}
算法
网络流二分图匹配的两道例题,和无源汇上下界可行流的例题
打算把acwing的课看完之后,先把还没有完成的网络流24题刷完,然后随缘看看区域赛啥的了