2024.2.16
寄
算是比较难的树形dp了吧。。。
我的跟题解做法不太一样,是维护2个数组 \(dp_{0/1,i}\) 和 \(f_{0/1,i}\)。不太好说,看题解做法吧QAQ。
原神
#include <bits/stdc++.h>
typedef long long ll;
const ll SIZE = 10000 + 100;
ll N, M, a[SIZE];
ll C;
ll cnt = 1, head[SIZE], next[SIZE * 2], to[SIZE * 2];
ll value[SIZE * 2];
ll key[SIZE];
ll dp[2][SIZE];//dp[0][now] 表示now为中转站的最小花费 dp[1][now] 表示now不为中转站的最小花费
void AddEdge(ll u, ll v, ll w) {
++ cnt;
next[cnt] = head[u];
head[u] = cnt;
to[cnt] = v;
value[cnt] = w;
}
ll fat[SIZE];
ll f[2][SIZE];//f[0][now] 表示从儿子来的最小花费 f[1][now] 表示从父亲来的最小花费
void Dfs1(ll now, ll fa, ll dis) {
ll sum = 0;
f[0][now] = 0;
for (ll i = head[now]; i; i = next[i]) {
if (to[i] == fa)
continue;
Dfs1(to[i], now, dis + value[i]);
sum += f[0][to[i]];
}
f[0][now] = std::min(sum + dis * key[now], std::min(dp[0][now], dp[1][now]));
}
std::pair<ll, ll> no;
ll temp[SIZE];
void Dfs3(ll now, ll fa, ll dis) {
ll sum = 0;
temp[now] = 0;
for (ll i = head[now]; i; i = next[i]) {
if (to[i] == fa || to[i] == no.first || to[i] == no.second)
continue;
Dfs3(to[i], now, dis + value[i]);
sum += temp[to[i]];
}
temp[now] = std::min(sum + dis * key[now], std::min(dp[0][now], dp[1][now]));
}
void Dfs2(ll now, ll fa, ll dis, ll from) {
Dfs3(from, fat[from], dis);
f[1][now] += temp[from];
dp[0][from] = std::min(dp[0][from], f[1][now] + dp[1][now]);
for (ll i = head[now]; i; i = next[i]) {
if (to[i] == fa)
continue;
Dfs2(to[i], now, dis + value[i], from);
}
return;
}
void Dfs(ll now, ll fa) {
ll tot = 0;
dp[0][now] = dp[1][now] = 1e18;
fat[now] = fa;
for (ll i = head[now]; i; i = next[i]) {
if (to[i] == fa)
continue;
tot ++;
Dfs(to[i], now);
}
if (!tot) {
dp[1][now] = C;
return;
}
else {
Dfs1(now, fa, 0);
dp[1][now] = f[0][now] + C;
}
no.first = fa;
for (ll i = head[now]; i; i = next[i]) {
if (to[i] == fa)
continue;
no.second = to[i];
Dfs2(to[i], now, value[i], now);
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
freopen("post.in", "r", stdin);
freopen("post.out", "w", stdout);
std::cin >> N >> M >> C;
for (ll i = 1, u, v, w; i < N; ++ i) {
std::cin >> u >> v >> w;
AddEdge(u, v, w);
AddEdge(v, u, w);
}
for (ll i = 1; i <= M; ++ i) {
std::cin >> a[i];
key[a[i]] ++;
}
Dfs(1, 0);
std::cout << std::min(dp[0][1], dp[1][1]) << '\n';
return 0;
}
摆
不会行列式和杜教筛
润
这个题好啊,我西索一下。
先知道一个性质:在分治过程中,每一层的长度只有两种可能。可以用归纳证一下。
我们先考虑单独特殊情况:选出的数是2的次幂(如:\(1000_{(2)},100_{(2)}\))。
那么我们的分治过程如下图:
那我们这个时候如果给 \(100_{(2)}\) 加上 \(1\) 呐?
那么就成了下图:
继续加一
有没有发现一些规律???
我们先设每一层原本的二进制长度为 \(p\)。(如:\(1001_{(2)}\) 的 \(p\) 为 \(4\))。并且设 \(x=10100_{(2)}\)
那么对于一个数 \(x\),我们在每一位上从左往右数第 \(2\) 个 \(1\) 到最低位所表示的数(以 \(x\) 为例,是 \(100_{(2)}\),即 \(4_{(10)}\)),是在每一层里面产生的贡献是等效于 \(2^{p}\) 的数的个数 (与每一层的节点数取min),每一层里面剩下的节点都等效于 \(2^{p-1}\)。
然后用线段树维护就行了。
崩坏:星穹铁道
#include <bits/stdc++.h>
typedef long long ll;
const int SIZE = 1e5 + 100;
const int mod = 998244353;
class Node {
public:
int l, r;
ll sum[2];
int first[2];
};
int N, M;
int s[SIZE];
ll power2[SIZE];
class SegmentTree {
#define lid (id << 1)
#define rid (id << 1 | 1)
public:
int tag[SIZE * 4];
Node node[SIZE * 4];
private:
Node pushup(Node lson, Node rson) {
Node result;
result.l = lson.l;
result.r = rson.r;
result.first[0] = std::min(lson.first[0], rson.first[0]);
result.first[1] = std::min(lson.first[1], rson.first[1]);
result.sum[0] = (lson.sum[0] * power2[rson.r - rson.l + 1] % mod + rson.sum[0]) % mod;
result.sum[1] = (lson.sum[1] * power2[rson.r - rson.l + 1] % mod + rson.sum[1]) % mod;
return result;
}
void Zero(Node &a) {
a.first[0] = 1e9;
a.sum[0] = 0;
a.first[1] = a.l;
a.sum[1] = (power2[a.r - a.l + 1] - 1 + mod) % mod;
}
void One(Node &a) {
a.first[0] = a.l;
a.sum[0] = (power2[a.r - a.l + 1] - 1 + mod) % mod;
a.first[1] = 1e9;
a.sum[1] = 0;
}
void Swap(Node &a) {
std::swap(a.first[0], a.first[1]);
std::swap(a.sum[0], a.sum[1]);
}
void Cover(int id, int New) {
if (New == 1) {
if (tag[id] == 2)
One(node[id]), New = 3;
else if (tag[id] == 3)
Zero(node[id]), New = 2;
else {
Swap(node[id]);
if (tag[id])
New = 0;
}
}
else {
if (New == 2)
Zero(node[id]);
else
One(node[id]);
}
tag[id] = New;
}
void pushdown(int id) {
if (tag[id] == 0)
return;
Cover(lid, tag[id]);
Cover(rid, tag[id]);
tag[id] = 0;
}
public:
void build(int id, int l, int r) {
if (l == r) {
if (s[l] == 1) {
node[id].first[0] = l;
node[id].first[1] = 1e9;
node[id].sum[0] = 1;
node[id].sum[1] = 0;
}
else {
node[id].first[0] = 1e9;
node[id].first[1] = l;
node[id].sum[0] = 0;
node[id].sum[1] = 1;
}
node[id].l = node[id].r = l;
return;
}
int mid = (l + r) >> 1;
build(lid, l, mid);
build(rid, mid + 1, r);
node[id] = pushup(node[lid], node[rid]);
return;
}
void Update(int id, int l, int r, int askL, int askR, int flag) {
if (askL <= l && r <= askR) {
Cover(id, flag);
return;
}
int mid = (l + r) >> 1;
pushdown(id);
if (askL <= mid)
Update(lid, l, mid, askL, askR, flag);
if (mid + 1 <= askR)
Update(rid, mid + 1, r, askL, askR, flag);
node[id] = pushup(node[lid], node[rid]);
}
Node Query(int id, int l, int r, int askL, int askR) {
if (askL <= l && r <= askR)
return node[id];
int mid = (l + r) >> 1;
pushdown(id);
Node result;
result.l = result.r = result.first[0] = result.first[1] = 1e9;
result.sum[0] = result.sum[1] = 0;
if (askL <= mid) {
result = Query(lid, l, mid, askL, askR);
}
if (mid + 1 <= askR) {
if (result.l == 1e9)
result = Query(rid, mid + 1, r, askL, askR);
else
result = pushup(result, Query(rid, mid + 1, r, askL, askR));
}
return result;
}
#undef lid
#undef rid
}tree;
std::string str;
ll treeSum[SIZE], prefix[SIZE], powerSum[SIZE];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
freopen("run.in", "r", stdin);
freopen("run.out", "w", stdout);
std::cin >> N >> M;
power2[0] = 1;
for (int i = 1; i <= N; ++ i) {
power2[i] = power2[i - 1] * 2 % mod;
treeSum[i] = (treeSum[i - 1] * 2 % mod + (1 + i - 1) * power2[i - 1] % mod) % mod;
powerSum[i] = (powerSum[i - 1] + power2[i - 1]) % mod;
prefix[i] = (prefix[i - 1] + power2[i - 1] * (1 + i - 1)) % mod;
}
std::cin >> str;
std::reverse(str.begin(), str.end());
for (int i = 1; i <= N; ++ i) {
s[i] = str[i - 1] - '0';
}
tree.build(1, 1, N);
for (int i = 1, opt, l, r; i <= M; ++ i) {
std::cin >> opt >> l >> r;
l = N - l + 1;
r = N - r + 1;
std::swap(l, r);
if (opt == 4) {
Node first = tree.Query(1, 1, N, l, r);
if (first.first[0] == 1e9) {
std::cout << 0 << '\n';
continue;
}
ll answer = treeSum[first.r - first.first[0] + 1];
Node second = tree.Query(1, 1, N, first.first[0] + 1, r);
if (second.first[0] == 1e9) {
std::cout << answer << '\n';
continue;
}
ll val = second.sum[0];
ll len1 = first.r - first.first[0] + 1;
ll len2 = first.r - second.first[0] + 1;
ll temp = power2[len1 - 1] * (len1 + len1 - len2 + 1) % mod * (len2) % mod * 499122177 % mod;
temp = (temp + prefix[len1 - len2] * val % mod) % mod;
answer = (answer - temp + mod) % mod;
temp = (temp + power2[len1 - 1] * len2 % mod) % mod;
temp = (temp + powerSum[len1 - len2] * val % mod) % mod * 2 % mod;
answer = (answer + temp) % mod;
answer = (answer + 2 * val) % mod;
std::cout << answer << '\n';
}
else {
tree.Update(1, 1, N, l, r, opt);
}
}
return 0;
}
/*
5 1
01010
4 2 5
*/