篠塚真佑実的树

给定\(n\)个节点的树,其中\(m\)个节点存在传送门,当飞船经过存在传送门的节点的时候,可以选择无消耗地传送至其他存在传送门的节点,现在有\(q\)次询问,每次询问给出起点\(st\)和终点\(ed\),若每艘飞船在飞行中最多只能进行一次传送,请你输出每次询问从起点到终点的最短路径长度

\(1<=m<=n<=2e5,1<=q<=2e5\)

题解:树上倍增 + \(LCA\) + 最短路

我们可以将情况分为两类:

  1. 不进行传送:那么我们只要利用树上倍增求出起点和终点的\(lca\)即可,然后维护点到根节点距离即可快速求出起点和终点之间的最短距离
  2. 进行1次传送:我们可以把所有存在传送门的节点看成一个源点(有点类似缩点),然后利用\(dij\)求出其他点到传送门的最短距离\(dis\),那么利用一次传送后起点和终点之间的最短距离为:\(dis[st]+dis[ed]\)

我们对于这两种情况取\(min\)即可

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10; int n, m;
vector<pii> g[N];
int a[N];
int fa[N][22], dep[N];
int dis[N];
int dis1[N];
int vis[N];
int st[N]; void dfs(int u, int par, int w)
{
dep[u] = dep[par] + 1;
fa[u][0] = par;
dis[u] = dis[par] + w;
for (int i = 1; i <= 20; ++i)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (auto &[v, w] : g[u])
{
if (v == par)
continue;
dfs(v, u, w);
}
} int lca(int u, int v)
{
if (dep[u] < dep[v])
swap(u, v);
for (int i = 20; i >= 0; i--)
{
if (dep[fa[u][i]] >= dep[v])
u = fa[u][i];
}
if (u == v)
return u;
for (int i = 20; i >= 0; i--)
{
if (fa[u][i] != fa[v][i])
{
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][0];
} void dij()
{
for (int i = 1; i <= n; ++i)
dis1[i] = INF, vis[i] = 0;
priority_queue<pii, vector<pii>, greater<pii>> q;
for (int i = 1; i <= m; ++i)
{
int v = a[i];
dis1[v] = 0;
q.push({dis1[v], v});
}
while (q.size())
{
int u = q.top().second;
q.pop();
if (vis[u])
continue;
vis[u] = 1;
for (auto [v, w] : g[u])
{
if (dis1[v] > dis1[u] + w)
{
dis1[v] = dis1[u] + w;
q.push({dis1[v], v});
}
}
}
} void solve()
{
cin >> n >> m;
for (int i = 1; i <= m; ++i)
cin >> a[i];
for (int i = 1, u, v, w; i < n; ++i)
{
cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
dij();
dfs(1, 0, 0);
int q;
cin >> q;
while (q--)
{
int s, ed;
cin >> s >> ed;
int ans = INF;
int rt = lca(s, ed);
ans = min(ans, dis[s] + dis[ed] - 2 * dis[rt]);
int d = dis1[s] + dis1[ed];
ans = min(ans, d);
cout << ans << endl;
}
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}

树上Minmax

给定一棵树,其包含 \(n\) 个结点和 \(n − 1\) 条边,每条边连接两个结点,且任意两个结点间有且仅有一条简单路径互相可达,每个点存在点权\(a_i\)

每次操作可以断一条边,随后选择分裂出的两棵树中的一棵树删除,请最小化操作次数使得最后剩下的树的点权和最大。

\(1<=n<=4*10^5\)

\(-10^9<=a_i<=10^9\)

题解:树形\(dp\)

不妨令根为\(1\),我们维护每个节点子树中的最大点权和以及得到该最大点权和所需的最小操作次数,对于任意节点\(u\)来说,存在\(3\)种情况:

  1. 子树\(v\)中的最大点权和\(<0\),显然我们要维护的是\(u\)子树内最大的点权和,所以如果我们不断开\(u\)和\(v\)之间的边,那么最大点权和会变小,所以我们贪心的将其断开,操作次数加\(1\)
  2. 子树\(v\)的最大点权和\(>0\),那么我们贪心的将其收入囊中,我们不选择断开,所以\(u\)节点需要加上\(v\)节点的最大点权和以及将\(v\)节点变成最大点权和的操作次数
  3. 子树\(v\)的最大点权和\(=0\),显然对于\(u\)的最大点权和来说我要不要都行,但是我们需要最小化操作次数,所以如果我们删去\(u\)和\(v\)之间的边,操作次数加\(1\),如果说子树\(v\)中的最小操作次数\(<1\),我们可以选择不删除这条边,否则我们为了维护最小操作次数选择删除\(u\)和\(v\)之间的边

那么最大我们遍历所有节点子树中最大的点权和,如果节点\(u\)中存在最大点权和,但是\(u\)不是根节点,我们需要断开其和根节点的一条边,使其成为连通块;如果\(u\)本身就是根节点,那么不受影响

状态表示:\(f[u][0/1]\):\(0\)代表\(u\)节点子树中的最大点权和,1代表最小操作次数

状态转移:按照上面分析的情况转移即可

if (f[v][0] < 0)
f[u][1]++;
else if (f[v][0] > 0)
{
f[u][0] += f[v][0];
f[u][1] += f[v][1];
}
else if (f[v][0] == 0)
{
if (f[v][1] < 1)
{
f[u][0] += f[v][0];
f[u][1] += f[v][1];
}
else
f[u][1]++;
}

状态初始:\(f[u][0] = a_u,f[u][1] = 0\)

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 4e5 + 10, M = 4e5 + 10; int n;
int a[N];
int f[N][2];
vector<int> g[N]; void dfs(int u, int par)
{
f[u][0] = a[u];
f[u][1] = 0;
for (auto v : g[u])
{
if (v == par)
continue;
dfs(v, u);
if (f[v][0] < 0)
f[u][1]++;
else if (f[v][0] > 0)
{
f[u][0] += f[v][0];
f[u][1] += f[v][1];
}
else if (f[v][0] == 0)
{
if (f[v][1] > 1)
f[u][1]++;
else
{
f[u][0] += f[v][0];
f[u][1] += f[v][1];
}
}
}
} void solve()
{
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
for (int i = 1, u, v; i < n; ++i)
{
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
int maxx = -INF;
for (int i = 1; i <= n; ++i)
{
maxx = max(f[i][0], maxx);
}
int cnt = INF;
for (int i = 1; i <= n; ++i)
{
if (f[i][0] == maxx)
{
if (i != 1)
cnt = min(cnt, f[i][1] + 1);
else
cnt = min(cnt, f[i][1]);
}
}
cout << maxx << " " << cnt << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}

树上MinmaxⅡ

给定一棵根为 \(1\) 的树,其包含 \(n\) 个结点和 \(n − 1\) 条边,每条边连接两个结点,且任意两个结点间有且仅有一条简单路径互相可达。在这里,我们认为当 \(n=1\) 时,根节点也是叶节点。

我们定义一个子节点都是叶节点且子树都被打上标记的节点为 “枝” ,被标记的叶节点都是 “花” 。在任何时刻你都可以选择一个 “枝” ,把它的所有子节点,也即 “花” 折下来。操作过后,会删除选定的 “枝” 和它的 “花” 间的连边, 选定的 “枝” 会变成新的 “花” 。

在每个时间点你都可以选取一个没有被标记过的节点来打上标记,最开始没有节点被标记。

你的目标是将树变为一枝 “花” 并最小化任何时间点树上被标记点数量的最大值,并求出该最小化后的最大值

题解:树形\(dp\) + 贪心 \(O(nlogn)\)

首先如果要将任意节点\(u\)变为花节点,我们必须将其所有子节点\(v\)变为花节点,即叶子节点,然后将\(u\)节点打上标记后\(u\)就变成了枝节点,然后我们就可以将花节点\(v\)折下使得\(u\)变为花节点;我们发现我们在将\(u\)的子节点\(v\)变为花的过程中,假设\(u\)有\(k\)个子节点,我们不妨先将子节点\(v_1\)变为花,那么在将\(v_2\)变为花的时候\(v_1\)一直处于标记状态,对于\(v_3\)来说,将其变为花的时候,\(v_1,v_2\)处于标记状态,令\(f[u]\)为将\(u\)节点变为花的所需的被标记点的数量,那么我们可以得到规律:将\(v_i\)变为花的时刻树上被标记的节点数位\(f[v_i]+i-1\),那儿我们是要最小化树上任意时刻被标记点的最大值,我们只需贪心地先将最大的子树变为花,然后次大.....最后选择最小的子树变为花即可,对于贪心我们只需对子节点的\(dp\)值进行排序即可

注意最后将\(u\)的所有子节点变为花后,我们还需要标记\(u\),那么此时场上被标记的节点数为\(sz[u]+1\),\(sz[u]\)代表\(u\)的子节点数

状态表示:\(f[u]\):将\(u\)节点变为花的所需的被标记点的最大数量

状态属性:\(MAX\)

状态转移:

\[f[u] = max(f[v_1],f[v_2]+1...f[v_i]+i-1,sz[u]+1)\\
f[v_1]>=f[v_2]>=...f[v_i]
\]

状态初始:\(f[u] = 1\)

答案呈现:遍历\(f[u]\)后找到最大值即可

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 4e5 + 10, M = 4e5 + 10; int n;
int f[N];
vector<int> g[N]; void dfs(int u, int par)
{
f[u] = 1;
vector<int> a;
for (auto v : g[u])
{
if (v == par)
continue;
dfs(v, u);
a.push_back(f[v]);
}
sort(all(a), greater<int>());
for (int i = 0; i < a.size(); ++i)
f[u] = max(f[u], a[i] + i);
f[u] = max(f[u], (long long)(a.size() + 1));
a.clear();
} void solve()
{
cin >> n;
for (int i = 1, u, v; i < n; ++i)
{
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
int ans = -INF;
for (int i = 1; i <= n; ++i)
ans = max(ans, f[i]);
cout << ans << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}

千里朱音的数

\[(\sum_{p_1\times p_2\times p_3<=k,1<=p_1,p_2,p_3<=n,p_1,p_2,p_3∈N^+} p_1\times p_2\times p_3)\ mod \ 998244353\\
1<=n,k<=10^9
\]

题解:思维 + 数论 : 还是蛮有套路的,多学学

不妨我们令\(p_1<=p_2<=p_3\)

此时我们发现\(p_1\)的上限为\(min(\sqrt[3]{k},n)\),即\(p_1\)的范围\([1,min(\sqrt[3]{k},n)]\)那么\(p_2\)的上限为\(min(\sqrt{\frac{k}{p_1}},n)\),即\(p_2\)的范围为\([p_1,min(\sqrt{\frac{k}{p_1}},n)]\),那么对于\(p_3\)来说上限为\(min(\frac{k}{p_1p_2},n)\),即\(p_3\)范围为\([p_2,min(\frac{k}{p_1p_2},n)]\)

我们打表后发现可以利用求和公式快速算出\(p_3\)的贡献:\(p_1*p_2*\sum p_3\),所以我们只需要枚举\(p_1,p_2\)即可,时间复杂度为\(O(k^{\frac{2}{3}})\)

但是我们只枚举了\(p_1<=p_2<=p_3\),实际上少计算了答案,我们考虑以下三种情况:

  1. 若\(p_1=p_2=p_3\),此时我们没有少算答案,不需要多加
  2. 若\(p_1<=p_2=p_3\ or\ p_1=p_2<=p_3\),对于这两种情况我们需要对贡献乘以\(C_3^1\)
  3. 若\(p_1<p_2<p_3\),对于这种情况我们需要对贡献乘\(A_3^3\)


#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10; int n, k; void solve()
{
cin >> n >> k;
int ans = 0;
for (int p1 = 1; p1 <= min((int)cbrt(k), n); ++p1)
{
for (int p2 = p1; p2 <= min((int)sqrt(1.0 * k / p1), n); ++p2)
{
int L = p2;
int R = min((int)(1.0 * k / (p1 * p2)), n);
if (R >= L)
{
if (p1 == p2)
{
ans = (ans % mod + ((p1 % mod) * (p2 % mod) * (p2 % mod)) % mod) % mod;
if (R >= L + 1)
ans = (ans % mod + ((p1 % mod) * (p2 % mod) * (((L + 1 + R) * (R - L) / 2) % mod) * 3) % mod) % mod;
}
else if (p1 < p2)
{
ans = (ans % mod + ((p1 % mod) * (p2 % mod) * (p2 % mod) * 3) % mod) % mod;
if (R >= L + 1)
ans = (ans % mod + ((p1 % mod) * (p2 % mod) * (((L + 1 + R) * (R - L) / 2) % mod) * 6) % mod) % mod;
}
}
}
}
cout << ans << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}

ZSTU2023校赛的更多相关文章

  1. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  2. SCNU省选校赛第二场B题题解

    今晚的校赛又告一段落啦,终于"开斋"了! AC了两题,还算是满意的,英语还是硬伤. 来看题目吧! B. Array time limit per test 2 seconds me ...

  3. 2014上半年acm总结(1)(入门+校赛)

    大一下学期才开始了acm,不得不说有一点迟,但是acm确实使我的生活充实了很多,,不至于像以前一样经常没事干=  = 上学期的颓废使我的c语言学的渣的一笔..靠考前突击才基本掌握了语法 寒假突然醒悟, ...

  4. 2017CUIT校赛-线上赛

    2017Pwnhub杯-CUIT校赛 这是CUIT第十三届校赛啦,也是我参加的第一次校赛. 在被虐到崩溃的过程中也学到了一些东西. 这次比赛是从5.27早上十点打到5.28晚上十点,共36小时,中间睡 ...

  5. HZNU第十二届校赛赛后补题

    愉快的校赛翻皮水! 题解 A 温暖的签到,注意用gets #include <map> #include <set> #include <ctime> #inclu ...

  6. 校赛F

    问题描述 例如对于数列[1 2 3 4 5 6],排序后变为[6 1 5 2 4 3].换句话说,对于一个有序递增的序列a1, a2, a3, ……, an,排序后为an, a1, an-1, a2, ...

  7. PKU2018校赛 H题 Safe Upper Bound

    http://poj.openjudge.cn/practice/C18H 题目 算平均数用到公式\[\bar{x}=\frac{x_1+x_2+x_3+\cdots+x_n}{n}\] 但如果用in ...

  8. 【魔改】hdu6325 多校赛3G xy排序凸包+llvector模板

    凸包算法前的预处理,可以极角排序,也可以按X,Y轴排序, 极角排序需要找到角落里的一个点,Xy轴排序要跑两遍凸包 而本题的要求只要一个上半凸包,并且有X轴从小到大以及字典序限制,完全符合xy排序,直接 ...

  9. 牛客网多校赛第9场 E-Music Game【概率期望】【逆元】

    链接:https://www.nowcoder.com/acm/contest/147/E 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524 ...

  10. BZOJ 3093: [Fdu校赛2012] A Famous Game

    3093: [Fdu校赛2012] A Famous Game Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 242  Solved: 129[Subm ...

随机推荐

  1. div中多行内容垂直居中显示

    div中多行内容垂直居中显示 一.css 样式 .wrap { height: 200px; width: 200px; border: 1px solid #232323; display: fle ...

  2. vue源码解析-实现一个基础的MVVM框架

    基本介绍 vue.js采用数据劫持结合发布-订阅模式的方式,通过Object.defineProperty()来劫持各个属性的getter,setter,在数据变动时发布消息给订阅者,触发响应的监听回 ...

  3. 扫描全能王启动鸿蒙原生应用开发,系HarmonyOS NEXT智能扫描领域首批

    近期,"鸿蒙合作签约暨扫描全能王鸿蒙原生应用开发启动仪式"(简称"签约仪式")正式举行.合合信息与华为达成鸿蒙合作,旗下扫描全能王将基于HarmonyOS NE ...

  4. DOM – IntersectionObserver

    介绍 IntersectionObserver 的作用是监听某个元素是否出现在框内 (比如 viewport). 它可以实现 lazy load image, 一开始图片是没有加载的, 当图片出现在 ...

  5. Fluent Builder 模式

    前言 以前最讨厌设计复杂方法调用, 就是那种需要一堆有逻辑规则的 config 作为参数的方法. 这种 config 通常是一个大对象, 有许多 property, property 之间有存在一些逻 ...

  6. sign与unsigned的原理、数据存储与硬件的关系

    目录 关键字unsigned和signed 数据在计算机中的存储 原码 与 补码的转化与硬件关系 原,反,补的原理: 整型存储的本质 变量存取的过程 类型目前的作用 十进制与二进制快速转换 大小端字节 ...

  7. C++ string类型常用操作

    string类型操作 字符串切割 str.substr(索引,切割的个数)  ->  返回字符串 注意:第二个参数为切割的个数 string buf = "abcdefg"; ...

  8. [OI] 整体二分

    整体二分可以理解成普通二分改版,其实并没有改多少,并且一般对 check() 函数的复杂度要求更宽松 先来看一道经典题目:求区间排名 给一个数列,若干组询问 \((l,r,k)\),求 \([l,r] ...

  9. Android内存分析命令

    一.内存指标 Item 全称 含义 等价 USS Unique Set Size 物理内存 进程独占的内存 PSS Proportional Set Size 物理内存 PSS= USS+ 按比例包含 ...

  10. 12 Masked Self-Attention(掩码自注意力机制)

    博客配套视频链接: https://space.bilibili.com/383551518?spm_id_from=333.1007.0.0 b 站直接看 配套 github 链接:https:// ...