暑假集训五[星际旅行,砍树,超级树,求和]
暑假集训五
题面
别问为啥先更六再更五。
A.星际旅行
转化题意,因为无向边相当于两条有向边,让一条无向边只走一次,相当于删去它两条边中的一条。则题意可转化为删除两条边之后,原图是否存在欧拉路,以及能形成欧拉路的方案数。
存在欧拉路的充要条件是:图是边联通的。
边联通和点联通的关系就像割点与割边。
删去两条边能构成欧拉路的,总共只有三种情况:
- 删除两个自环。
- 删除一个自环和一个正常边。
- 删除两条有且仅有一个公共顶点的边。
正确性的证明详见kiritokazuto大佬的博客。
Code
#include<cstdio> #include<algorithm> using namespace std; const int MAXN = 1e5 + 10; int n, m, cnt, ring; long long ans; int head[MAXN], deg[MAXN]; bool used[MAXN]; struct Edge{ int to, next; }e[MAXN << 1]; inline void Add(int u, int v){ e[++cnt].to = v; e[cnt].next = head[u]; head[u] = cnt; } void dfs(int rt){ used[rt] = true; for(register int i = head[rt]; i; i = e[i].next){ int v = e[i].to; if(!used[v]) dfs(v); } } inline int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9'){ if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9'){ x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int main(){ n = read(), m = read(); for(register int i = 1; i <= m; i++){ int u, v; u = read(), v = read(); Add(u, v); Add(v, u); if(u == v) ring++; else{ deg[u]++; deg[v]++; } } if(ring == m){ putchar('0'); return 0; } for(register int i = 1; i <= n; i++){ if(deg[i]){ dfs(i); break; } } for(register int i = 1; i <= n; i++){ if(!used[i] && deg[i]){ putchar('0'); return 0; } } ans += 1LL * (m - ring) * ring; ans += 1LL * ring * (ring - 1) / 2; for(register int i = 1; i <= n; i++) ans += 1LL * deg[i] * (deg[i] - 1) / 2; printf("%lld", ans); return 0; }
B.砍树
数论分块,等价于求出一个最大的 ,满足:
设 。
那么 。
然后就可以数论分块了。
Code
#include<cmath> #include<cstdio> #define LL long long using namespace std; const int MAXN = 110; LL n, k; LL sum, ans; LL aim[MAXN]; inline LL read(){ LL x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9'){ if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9'){ x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int main(){ //数论分块啊 n = read(), k = read(); for(register int i = 1; i <= n; i++){ aim[i] = read(); sum += aim[i]; } sum += k; for(register LL l = 1, r; l <= sum; l = r + 1){ r = sum / (sum / l); LL tot = 0; for(register int i = 1; i <= n; i++) tot += ceil((1.0 * aim[i]) / (1.0 * r)); if(tot <= sum / r) ans = r; } printf("%lld", ans); return 0; }
C.超级树
神仙DP,不会,贺的。
还是看kiritokazuto大佬的博客吧。
Code
#include<cstdio> #include<algorithm> #define LL long long using namespace std; const int MAXK = 310; int k, p; LL dp[MAXK][MAXK]; //这状态定义能想到就TM见鬼了 // dp[i][j]表示在一棵 i 级超级树中,有 j 条路径同时存在且这 j 条路径没有公共点时,可能的情况数 int main(){ scanf("%d%d", &k, &p); dp[1][1] = dp[1][0] = 1; for(register int i = 0; i <= k; i++){ for(register int j = 0; j <= k; j++){ for(register int l = 0; l <= j; l++){ int r = j - l; LL num = dp[i][l] * dp[i][r] % p; dp[i + 1][j + 1] = (1LL * dp[i + 1][j + 1] + num) % p; dp[i + 1][j] = (1LL * dp[i + 1][j] + num + 2 * num * j) % p; dp[i + 1][j - 1] = (1LL * dp[i + 1][j - 1] + 2 * num * l * r + num * (l * (l - 1) + r * (r - 1))) % p; } } } printf("%lld", dp[k][1]); return 0; }
D.求和
P4427 [BJOI2018]求和
大水题,LCA树剖维护 + 树上差分。
可以观察到,,,并不大。
一棵树的平均深度是 ,退化成链的最坏深度是 ,所以我们可以预处理出 ~ 的 ~ 次幂,同时维护一个 ~ 次幂的树上前缀和。
最后的答案就是
记得取模后被减数可能小于减数,所以减完要先加上模数再取模,考试的时候因为这个 100 pts →
0 pts。
Code
//没大样例,手搓的倒是没啥问题 //linux怎么架对拍来着,囸 //卡个常,毕竟大常数选手 #include<cstdio> #include<algorithm> #define LL long long using namespace std; const int MAXN = 3e5 + 10, MAXK = 55; const int Mod = 998244353; int n, m, cnt, num; int head[MAXN]; int fa[MAXN], son[MAXN], size[MAXN], deep[MAXN]; int dfn[MAXN], top[MAXN]; LL p[MAXN][MAXK], sum[MAXN][MAXK]; struct Edge{ int to, next; }e[MAXN << 1]; inline void Add(int u, int v){ e[++cnt].to = v; e[cnt].next = head[u]; head[u] = cnt; } void dfs_deep(int rt, int father, int depth){ size[rt] = 1; fa[rt] = father; deep[rt] = depth; for(register int i = 1; i <= 50; i++) sum[rt][i] = 1LL * (sum[fa[rt]][i] + p[deep[rt]][i]) % Mod; int max_son = -1; for(register int i = head[rt]; i; i = e[i].next){ int v = e[i].to; if(v == father) continue; dfs_deep(v, rt, depth + 1); size[rt] += size[v]; if(max_son < size[v]){ son[rt] = v; max_son = size[v]; } } } void dfs_top(int rt, int top_fa){ dfn[rt] = ++num; top[rt] = top_fa; if(!son[rt]) return; dfs_top(son[rt], top_fa); for(register int i = head[rt]; i; i = e[i].next){ int v = e[i].to; if(!dfn[v]) dfs_top(v, v); } } int Get_Lca(int x, int y){ while(top[x] != top[y]){ if(deep[top[x]] < deep[top[y]]) swap(x, y); x = fa[top[x]]; } if(deep[x] > deep[y]) swap(x, y); return x; } LL Get_Sum(int x, int y, int k){ int lca = Get_Lca(x, y); return (1LL * (sum[x][k] + sum[y][k]) % Mod - (2 * sum[lca][k] % Mod) + Mod + p[deep[lca]][k]) % Mod; } void init(){ for(register int i = 1; i <= n; i++){ p[i][0] = 1; for(register int j = 1; j <= 50; j++) p[i][j] = 1LL * p[i][j - 1] * i % Mod; } } inline int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9'){ if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9'){ x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int main(){ n = read(); init(); for(register int i = 1; i <= n - 1; i++){ int u, v; u = read(), v = read(); Add(u, v); Add(v, u); } dfs_deep(1, 0, 0); dfs_top(1, 1); m = read(); for(register int i = 1; i <= m; i++){ int x, y, k; x = read(), y = read(), k = read(); printf("%lld\n", 1LL * Get_Sum(x, y, k)); } return 0; }
以下为博客签名,与博文无关。
只要你们不停下来,那前面就一定有我。所以啊,不要停下来~
本文来自博客园,作者:TSTYFST,转载请注明原文链接:https://www.cnblogs.com/TSTYFST/p/16600332.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理