以下题解包括:

\[1002【HDU-6604】 \\ 1004【HDU-6606】 \\ 1006【HDU-6608】 \\ 1007【HDU-6609】 \\ 1009【HDU-6611】\]

【1002】 支配树(拓扑+LCA) HDU-6604 Blow up the city

http://acm.hdu.edu.cn/showproblem.php?pid=6604

给定一个 \(n\) 个点 \(m\) 条边的有向无环图,出度为 0 的定义为“中心城市”,现有 \(q\) 次询问,每次给定两个起点 \(a\) 和 \(b\),每次可以选择一个点去掉,问存在几种方案,使得 \(a\) 和 \(b\) 任意一个无法到达“中心城市”。

支配树板子题?alright,并不会。学了一下发现好像大概就是拓扑排序+LCA+新图的深度检测,大概就是这样吧。

建立反向图,将城市到若干个终点看成从若干个起点出发到某个城市,再用一个源点连接那些度为 0 的点,即可看成从源点出发到某个城市。要炸掉一个点使得无法到达某个城市,那么需要炸掉的是从 源点到该城市的必经点,考虑建立支配树,根据定义可知支配树到根的链上结点个数就是必经点的个数。两个城市的容斥减去 LCA 到根上这条链即可。由于保证是 DAG ,因此直接按拓扑序建树即可,建完树利用结点的 \(dep\) 来求点到根的链长,注意最后答案要减去一开始添加的源点。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 1e5+5;int n, m, q, cnt;
int dep[maxn], re_in[maxn];    // re_in 为反图点入度
int fa[maxn][20], tp[maxn];
vector<int> g[maxn], re_g[maxn], new_g[maxn];   // 正图、反图、新图void init() {for(int i = 0; i <= n; i++) {g[i].clear();re_g[i].clear();new_g[i].clear();dep[i] = 0;re_in[i] = 0;tp[i] = 0;for(int j = 0; j <= log2(n); j++) {fa[i][j] = 0;}}cnt = 0;
}void Topo() {queue<int> q;int rt = 0;     // 图可能不连通,建一个新点连接起来 for(int i = 1; i <= n; i++) {if(re_in[i] == 0) {q.push(i);new_g[rt].push_back(i);g[i].push_back(rt);}}while(!q.empty()) {int u = q.front();q.pop();tp[++cnt] = u;for(int i = 0; i < (int)re_g[u].size(); i++) {int v = re_g[u][i];re_in[v] --;if(re_in[v] == 0) {q.push(v);}}}
}int LCA(int x, int y) {if(dep[x] < dep[y]) {swap(x, y);}for(int i = 19; i >= 0; i--) {if(dep[x] - (1<<i) >= dep[y]) {x = fa[x][i];}}if(x == y) {return x;}for(int i = 19; i >= 0; i--) {if(fa[x][i] != fa[y][i]) {x = fa[x][i];y = fa[y][i];}}return fa[x][0];
}void build() {     // 建立支配树for(int i = 1; i <= cnt; i++) {int x = tp[i];int y = g[x][0];for(int j = 1; j < (int)g[x].size(); j++) {int z = g[x][j];y = LCA(y, z);}new_g[y].push_back(x);dep[x] = dep[y] + 1;fa[x][0] = y;for(int i = 1; i <= 19; i++) {fa[x][i] = fa[fa[x][i-1]][i-1]; }}
}int main() {int t;scanf("%d", &t);while(t--) {scanf("%d%d", &n, &m);init();for(int i = 1; i <= m; i++) {int u, v;scanf("%d%d", &u, &v);g[u].push_back(v);re_g[v].push_back(u);re_in[u] ++;}Topo();build();scanf("%d", &q);while(q--) {int a, b;scanf("%d%d", &a, &b);printf("%d\n", dep[a] + dep[b] - dep[LCA(a, b)]);}}return 0;
}

【1004】 二分+权值线段树 HDU-6606 Distribution of books

http://acm.hdu.edu.cn/showproblem.php?pid=6606

给定一个 \(n\) 个数,可以选择其中 \(m\) 个数(不限)分成 k 堆,问每堆数字和最大值最小是多少。

以离散化后前缀和的 \(rk\) 为下标,\(f\) (最多能分成的块数)为权值建立线段树。线段树需要实现单点修改、区间求最大值操作。

时间复杂度:\(o(n*log^2(n))\) (不过我的代码好像是... \(o(n*log^3(n))\) 的,不过也过了就是)。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const ll inf = 200000000000000 + 5;
const int mod = 1e9 + 7;const int maxn = 2e5+5;int n, k, new_n;
int MAX[maxn<<2];
ll a[maxn], sum[maxn];
vector<ll> v;int getid(ll x) {return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}void build(int l, int r, int rt) {MAX[rt] = -maxn;if(l == r) {return;}int mid = (l+r) >> 1;build(l, mid, rt<<1);build(mid+1, r, rt<<1|1);
}void update(int l, int r, int rt, int pos, int v) {if(l == r) {MAX[rt] = v;return ;}int mid = (l+r) >> 1;if(pos <= mid) {update(l, mid, rt<<1, pos, v);}else {update(mid+1, r, rt<<1|1, pos, v);}MAX[rt] = max(MAX[rt<<1], MAX[rt<<1|1]);
}int query(int L, int R, int l, int r, int rt) {if(l > r) {return -maxn;}if(L <= l && r <= R) {return MAX[rt];}int mid = (l+r) >> 1;int ans = -maxn;if(L <= mid) {ans = query(L, R, l, mid, rt<<1);}if(R > mid) {ans = max(ans, query(L, R, mid+1, r, rt<<1|1));}return ans;
}int check(ll x) {build(1, new_n, 1);int f;for(int i = 1; i <= n; i++) {int l = getid(sum[i]-x);int r = new_n;f = query(l, r, 1, new_n, 1) + 1;if(sum[i]-x <= 0) {f = max(f, 1);}if(f >= k) {return 1;}if(f > 0) {update(1, new_n, 1, getid(sum[i]), f);}}return 0;
}int main() {int t;scanf("%d", &t);while(t--) {v.clear();sum[0] = 0;scanf("%d%d", &n, &k);for(int i = 1; i <= n; i++) {scanf("%lld", &a[i]);sum[i] = sum[i-1] + a[i];v.push_back(sum[i]);}sort(v.begin(), v.end());v.erase(unique(v.begin(), v.end()), v.end());new_n = (int)v.size();v.push_back(inf);ll l = -inf;ll r = inf;while(l < r) {ll mid = (l+r) >> 1;int flag = check(mid);if(flag) {r = mid;}else {l = mid+1;}}printf("%lld\n", l);}return 0;
}

【1006】 数学 HDU-6608 Fansblog

http://acm.hdu.edu.cn/showproblem.php?pid=6608

队友敲的,雨我无瓜

给定一个数 \(p\) ,先找到比它小的第一个素数 \(q\),输出 \(q ! \ mod \ p\) 的答案。【\(1e^9 \leq p \leq 1e^{14}\)】

威尔逊定理 + 质数的密度分布。

威尔逊定理:判定一个自然数是否为素数的充分必要条件。即:当且仅当p为素数时:\((p - 1 )! ≡ -1 ( mod \ p )\)。

质数的密度分布:反正质数的差距大概不会超过2000。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#define mes(a, b) memset(a, b, sizeof a)
using namespace std;
typedef long long ll;
const int maxn = 1e7+10;
const int maxm = 1e6+10;
const int mod = 1e9+7;int num[maxn], p[maxm], cnt;
ll n;void init(){cnt = 0;for(ll i = 2; i < maxn; i++){if(!num[i]){p[++cnt] = i;for(ll j = i*i*1ll; j < maxn; j+=i){num[j] = 1;}}}
}ll mul(ll a, ll b) {ll ans = 0;while(b) {if(b&1) {ans = (ans + a) % n;}a = (a+a) % n;b /= 2;}return ans;
}ll pow(ll a, ll b){ll ans = 1;while(b){if(b&1)ans = mul(ans,a);a = mul(a, a);b /= 2;}return ans;
}int main(){init();ll m, t;scanf("%lld", &t);while(t--){scanf("%lld", &n);int flag = 1;for(m = n-1;flag; m--){flag = 0;for(int i = 1; i <= cnt && sqrt(m) >= p[i]; i++){if(m % p[i]*1ll == 0){flag = 1;break;}}if(flag == 0)break;}m++;ll num = 1;for(; m < n; m++){num = mul(num, m);}num = mul((n-1ll), pow(num, n-2));printf("%lld\n", num%n);}return 0;
}

【1007】 权值线段树 HDU-6609 Find the answer

http://acm.hdu.edu.cn/showproblem.php?pid=6609

给定 \(n\) 个数和 \(m\) 的总容量(\(a_i <= m\))。对于每一个 \(i (1 \leq i \leq n)\),最少需要把区间 \([1,i-1]\) 内多少个数变成0,才能使得 \(\sum^{i}_{j=1} \leq m\)。 输出 n 个数表示答案。

比赛时候现实优先队列+multiset优化,然后 T 了,毕竟最差情况复杂度到 \(n^2\) 了。然后想主席树,怎么都写不出来。然后被旁边大佬启发才发现是权值线段树,然后就在主席树板子上改了改,就过了。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 2e5 + 5;int n, m;
int a[maxn];
vector<int> v;      // 保存去重后的数组
struct node {int rk;ll sum;
} T[maxn * 4];int getid(int x) {  // 获取每次更新的节点的“位置”return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}void push_up(int rt) {T[rt].rk = T[2*rt].rk + T[2*rt+1].rk;T[rt].sum = T[2*rt].sum + T[2*rt+1].sum;
}void update(int l, int r, int rt, int x) {if(l == r) {T[rt].rk ++;T[rt].sum += v[l-1];return ;}int mid = (l+r) / 2;if(x <= mid) {update(l, mid, 2*rt, x);}else {update(mid+1, r, 2*rt+1, x);}push_up(rt);
}int query(int l, int r, int rt, int x) {if(l == r) {return x / v[l-1];}int mid = (l+r) / 2;if(T[2*rt].sum >= x) {return query(l, mid, 2*rt, x);}else {return T[2*rt].rk + query(mid+1, r, 2*rt+1, x-T[2*rt].sum);}
}int main() {int t;scanf("%d", &t);while(t--) {v.clear();memset(T, 0, sizeof(T));scanf("%d%d", &n, &m);for(int i = 1; i <= n; i++) {scanf("%d", &a[i]);v.push_back(a[i]);}sort(v.begin(), v.end());v.erase(unique(v.begin(), v.end()), v.end());   // 去重int new_n = (int)v.size();ll s = 0;for(int i = 1; i <= n; i++) {s += 1ll*a[i];if(s <= 1ll*m) {printf("0 ");update(1, new_n, 1, getid(a[i]));continue;}int x = m - a[i];int RANK = query(1, new_n, 1, x);printf("%d ", i-1-RANK);update(1, new_n, 1, getid(a[i]));}printf("\n");}return 0;
}

【1009】 权值线段树 HDU-6611 Find the answer

http://acm.hdu.edu.cn/showproblem.php?pid=6611

参考:https://blog.csdn.net/baiyifeifei/article/details/97706037

给出一段序列,可以从中找出最多 \(k\) 段不下降子序列,使得权值和最大。

建立源点汇点和序列点,将所有的点拆成两个,分为母点和子点,母源点对子源点连接容量为k,费用为0的边,序列母点向序列子点连接容量为1,费用为序列上该点的值的负值,所有的序列子点向其后的权值大于等于它的序列点的母点连接容量为1,费用为0的边,所有的序列子点向母汇点连容量为1,费用为0的边,母汇点向子汇点连容量为k,费用为0的边,跑出最小费用后取负值即是答案。

又被人人都会的网络流卡了。\(dijkstra\) 优化的最小费用最大流,原理还不是太懂,就从网上扣了一个板子下来,算是先挖个坑把。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = (2e3+5)*2;struct EDGE {int to, w, c, nxt;
};vector<EDGE> g[maxn];
int n, k, tot;  // tot 为拆点后的总点数
int a[maxn];
int H[maxn];
int dis[maxn];
int preV[maxn], preE[maxn]; // 前一个点,前一个边void addedge(int u, int v, int w, int c) {g[u].push_back({v, w, c, (int)g[v].size()});g[v].push_back({u, 0, -c, (int)g[u].size()-1});
}void MCMF(int s, int t, int f) {   // 最小费用最大流int ans = 0;memset(H, 0, sizeof(H));while(f) {priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>> > q;memset(dis, inf, sizeof(dis));dis[s] = 0;q.push(pair<int, int>(0, s));while(!q.empty()) {pair<int, int> x = q.top();q.pop();int u = x.second;if(dis[u] < x.first) {continue;}for(int i = 0; i < (int)g[u].size(); i++) {EDGE& e = g[u][i];if(e.w > 0 && dis[e.to] > dis[u]+e.c+H[u]-H[e.to]) {dis[e.to] = dis[u]+e.c+H[u]-H[e.to];preV[e.to] = u;preE[e.to] = i;q.push(pair<int, int>(dis[e.to], e.to));}}}if(dis[t] == inf) {break;}for(int i = 0; i <= tot; i++) {H[i] += dis[i];}int d = f;for(int v = t; v != s; v = preV[v]) {d = min(d, g[preV[v]][preE[v]].w);}f -= d;ans += d*H[t];for(int v = t; v != s; v = preV[v]) {EDGE& e = g[preV[v]][preE[v]];e.w -= d;g[v][e.nxt].w += d;}}printf("%d\n", -ans);
}int main() {int t;scanf("%d", &t);while(t--) {scanf("%d%d", &n, &k);for(int i = 2; i <= n+1; i++) {scanf("%d", &a[i]);     // 从 2 开始存}int ss = 0, s = 1;  // 源点拆int t = 2+2*n, tt = 2+2*n+1;    // 汇点拆tot = tt+1;for(int i = 0; i <= tot; i++) {g[i].clear();}addedge(ss, s, k, 0);   // 母源-->子源 连接容量为 k,费用为 0 的边addedge(t, tt, k, 0);   // 子汇-->母汇 连接容量为 k,费用为 0 的边for(int i = 2; i <= n+1; i++) {addedge(s, i, 1, 0);        // 子源 --> 序列母点 连接容量为 1,费用为 0 的边addedge(i, i+n, 1, -a[i]);  // 序列母点 --> 序列子点 连接容量为 1,费用为 -ai 的边addedge(i+n, t, 1, 0);      // 序列子点 --> 子汇 连接容量为 1,费用为 0 的边for(int j = i+1; j <= n+1; j++) {if(a[j] >= a[i]) {addedge(i+n, j, 1, 0);  // 满足条件 --> 前一点的子点到后一点的母点 连接容量为 1,费用为 0 的边}}}MCMF(ss, tt, inf);}return 0;
}

转载于:https://www.cnblogs.com/Decray/p/11294091.html

暑假N天乐【比赛篇】 —— 2019杭电暑期多校训练营(第三场)相关推荐

  1. 暑假N天乐【比赛篇】 —— 2019杭电暑期多校训练营(第一场)

    杭电多校第一场属实恐怖,我连补题的冲动都莫得了. 本来还想说按去年的经验来说,杭电是要比牛客稍微友好那么一丢丢的吧.结果当场打脸,签到题来了个最短路*2+网络流,这谁顶得住啊. 所以这两天都没在补这场 ...

  2. 暑假N天乐【比赛篇】 —— 2019杭电暑期多校训练营(第二场)

    这段时间自己都不知道该干些什么,比赛时候什么都想不到,全靠队友A题维持生计这样...补题进度也是一拖再拖,明后天又是两场连打,感觉又要堆一堆题了...看着补算了. 以下题解包括: \[1005[HDU ...

  3. 暑假N天乐【比赛篇】 —— 2019杭电暑期多校训练营(第四场)

    本来想说这场放掉了,算了还是补了吧... 以下题解包括: \[1001[HDU-6614] \\ 1003[HDU-6616] \\ 1007[HDU-6620] \\ 1008[HDU-6621] ...

  4. 暑假N天乐【比赛篇】 —— 2019杭电暑期多校训练营(第五场)

    开启疯狂水题解模式,大概会持续好几次...直到我赶上进度为止. 以下题解包括: \[1001[HDU-6624] \\ 1004[HDU-6627] \\ 1005[HDU-6628] \\ 1006 ...

  5. 暑假N天乐【比赛篇】 —— 2019杭电暑期多校训练营(第六场)

    我胡汉三又滚回来了....保质期过了的题也得记下来. 以下题解包括: \[1002[HDU-6635] \\ 1005[HDU-6638] \\ 1006[HDU-6639] \\ 1008[HDU- ...

  6. 2019牛客暑期多校训练营 第三场 I Median

    传送门 链接:https://ac.nowcoder.com/acm/contest/883/I 来源:牛客网 JSB has an integer sequence a1,a2,-,ana_1, a ...

  7. 暑假N天乐【比赛篇】 —— 2019牛客暑期多校训练营(第二场)

    最近几天都没写博客,真是没什么时间写了,专题卡着,一周四场比赛,场场爆零,补题都补傻了.第一场还差两题可能今天补掉吧,昨天的杭电也是完全没动,感觉...很烦 第二场牛客断断续续也是补了几天...大概一 ...

  8. 2019牛客暑期多校训练营(第一场)(B、C、E、F、H、I题待补、J)

    特别感谢 教我C题的杭电大佬.叉姐的题解(看了还是啥也不会) B.Integration(待定系数法) 求上述式子的值,输出对分数取模的值,ai<=1e9,n<=1e3 赛中的时候用裂项拆 ...

  9. 暑假N天乐【比赛篇】 —— 2019牛客暑期多校训练营(第一场)

    期待已久暑假的重头戏终于算是打响了第一枪吧...第一把不出所料的被吊打了...被满场的数学题和自己的菜鸡英语疯狂支配.补题的话,估计只能量力而行了吧 -- 一个题解都推不出来.标程都看不懂的鶸. A ...

最新文章

  1. 安卓开发8-WebView支持文件上传
  2. sentinel 官方文档_Sentinel控制台监控数据持久化到MySQL数据库
  3. 十分钟内学会 Python
  4. Android 截屏监听(截图分享功能实现)
  5. BZOJ 1398: Vijos1382寻找主人 Necklace(最小表示法)
  6. 【Python科学计算系列】行列式
  7. 【渝粤教育】电大中专职业健康与安全_1作业 题库
  8. Bootstrap 4:如何使顶部固定的Navbar保持在容器中而不拉伸?
  9. python中文件变化监控-watchdog
  10. java要频繁调用容器时_Java知识点梳理
  11. Linux之内核调试sysrq
  12. kafka java_Java操作Kafka
  13. 【机器学习】基于AutoEncoder的BP神经网络的tensorflow实现
  14. 手把手教您编写第一个单片机程序
  15. Activiti7 25张表含义
  16. php 盗链新浪图片_php实现博客,论坛图片防盗链的方法
  17. NDP调查:P2P下载的视频中60%为情色内容
  18. PR值计算公式带来的思考
  19. 微信小程序编译的错误解决办法:Error: accessSync:fail no such file or directory
  20. python培训总结心得

热门文章

  1. STM32连接HT1621段码屏驱动
  2. WIN10 注册表winlogon乱删除后果及系统恢复
  3. Python3 之 实现 Mp4 视频 转 Mp3 音频,从此解放 大会员
  4. 学计算机还是学生物,生物医学工程,到底是学生物,还是学医?
  5. RocketMq-dashboard:topic 5min trend 原理和源码分析(一)
  6. 视频监控在油气长输管道巡护管理的应用解决方案
  7. E. Boxers(贪心+移出多余空位)
  8. 加减乘除的计算机英语表示,英语数字加减乘除表示法
  9. 京东成华为新品首发阵地 智慧手机Mate 10发布
  10. 为什么要代码重构?如何重构?常见重构技巧,值得收藏!