文章目录

  • EOJ 2月月赛补题
    • A-昔我往矣
    • B-杨柳依依
    • C-今我来思(sì)
    • D- 雨雪霏霏

EOJ 2月月赛补题

总之就是非常爆炸

A-昔我往矣

题意: 现在有一棵树,每条边有边权,并且给你 555 个点 a,b,c,d,ea,b,c,d,ea,b,c,d,e 。如果从任一点出发能到达其它所有的点,求最小的边权和。

思路: 很明显,是个 LCALCALCA 。但是当时不会做。看了题解后才明白。五个点的公共祖先就是 dfsdfsdfs 序最大和最小的两个点的的 LCALCALCA。至于为什么呢?,按我的理解就是:在用 dfs+RMQdfs+RMQdfs+RMQ 求 LCALCALCA 时,求得是这两个点的dfs序之间深度最小的节点,而这里多个点时,就是找这五个点形成的最大的 dfsdfsdfs 区间中深度最小的点。

但是我写的时候是用倍增写的。顺带发现我那个常数优化过的倍增求LCA的板子好像裂了…这两天就去补 。

先将 dfsdfsdfs 序最大和最小的点的最短距离加起来,然后对剩下的点,分别与已经加上的点求 LCALCALCA ,找到深度最深的点,就能得到多出来的那一部分链的权值。直到全部加完。

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 1e6 + 10;
int n, m, root;
struct edge{int u, v;int cost;edge() {}edge(int _u, int _v, int _c) {u = _u; v = _v;cost = _c;}
};
vector<edge> G[N];
int fa[N][32], dep[N], lg[N]; //这里的lg是二进制位数
int L[N], R[N], dfsn[N]; //dfs序
int dis[N]; //节点i到根节点的距离
int dfs_cnt = 0;
void add_edge(int u, int v, int c) {G[u].push_back(edge(u, v, c));G[v].push_back(edge(v, u, c));
}
//void init(int _n) {//    dep[0] = 0; dis[0] = 0;
//    dfs_cnt = 0;
//    for (int i = 0; i <= _n; i++)
//        G[i].clear();
//
//    lg[0] = 0;
//    for (int i = 1; i <= 20; i++)
//        lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
//}
void dfs(int u, int last) {fa[u][0] = last;dep[u] = dep[last] + 1; //计算深度L[u] = ++dfs_cnt; //dfs序dfsn[dfs_cnt] = u;for (int i = 1; i < 32; i++)fa[u][i] = fa[fa[u][i - 1]][i - 1];for (int i = 0; i < (int)G[u].size(); i++) {int v = G[u][i].v;if (v == last) continue;dis[v] = dis[u] + G[u][i].cost;dfs(v, u);}R[u] = dfs_cnt;
}
int lca(int x, int y) {if (dep[x] < dep[y]) swap(x, y);
//    while (dep[x] > dep[y])
//        x = fa[x][lg[dep[x] - dep[y]] - 1];for (int i = 31; i >= 0; i--)if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];if (x == y) return x;for (int i = 31; i >= 0; i--)if (fa[x][i] != fa[y][i])x = fa[x][i], y = fa[y][i];return fa[x][0];
}
bool cmp(int x, int y) {return L[x] < L[y];
}
int main() {scanf("%d", &n);for (int i = 1; i < n; i++) {int u, v, c;scanf("%d%d%d", &u, &v, &c);u++, v++;add_edge(u, v, c);}dfs(1, 0);int q;scanf("%d", &q);while (q--) {vector<int> p;for (int i = 0; i < 5; i++) {int x; scanf("%d", &x); x++;p.push_back(x);}sort(p.begin(), p.end(), cmp);
//        for (int i = 0; i < 5; i++)
//            cout << dis[p[i]] << " ";
//        cout << "\n";int xp = lca(p[0], p[4]); //5个点的lcaint ans = dis[p[0]] + dis[p[4]] - 2 * dis[xp];vector<int> now;now.push_back(p[0]);now.push_back(p[4]);for (int i = 1; i < 4; i++) {int dot = 0;for (int j = 0; j < (int)now.size(); j++) {int nx = lca(p[i], now[j]);if (dot == 0 || dep[nx] > dep[dot])dot = nx;}ans += dis[p[i]] - dis[dot];now.push_back(p[i]);}printf("%d\n", ans);}return 0;
}/*
6
4 0 4
0 1 2
1 3 9
3 5 1
3 2 5
*/

B-杨柳依依

题意: 已知一个图,图上有 kkk 个人,每个人从点 aia_iai​ 到点 bib_ibi​ 。并且每次必定是走最短路径,若有多条最短路径,等概率选择一条。定义 Ei(w)E_i(w)Ei​(w) 表示第 iii 个人经过 www 的概率,现在要求出 ∑i=0k−1E(w)\sum_{i=0}^{k-1}E(w)∑i=0k−1​E(w) 最大的点 www。

思路: 对于每一对起点和终点都 bfsbfsbfs 一次,求出最短路长度以及个数。然后对于图上每一个点 ppp ,判断起点到点 ppp 的距离加上 终点到点 ppp 的概率是否与起点到终点距离相等,若相等,就说明这个点在最短路径上,然后这个点的总概率加上 起点到点p的最短路径数×终点到点p的最短路径数起点到终点的最短路径数\frac{\text{起点到点p的最短路径数} \times \text{终点到点p的最短路径数}}{\text{起点到终点的最短路径数}}起点到终点的最短路径数起点到点p的最短路径数×终点到点p的最短路径数​

最后遍历一遍所有点,找到最大的点就是答案。

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const int N = 2e5 + 10;
const int M = 2e5 + 10;
typedef pair<int, int> pii;
int cnt[2][N], d[2][N];
int n, m;
vector<int> G[N];
void add_edge(int u, int v) {G[u].push_back(v);G[v].push_back(u);
}
void bfs(int s, int t, int cnt[], int d[]) {for (int i = 0; i <= n; i++) {cnt[i] = 0;d[i] = INF;}d[s] = 0; cnt[s] = 1;queue<pii> q;q.push({0, s});while (q.size()) {pii now = q.front(); q.pop();int dd = now.first, u = now.second;if (dd > d[u]) continue;for (int v : G[u]) {if (d[v] > d[u] + 1) { //更新最短路d[v] = d[u] + 1;cnt[v] = cnt[u];q.push({d[v], v});}else if (d[v] == d[u] + 1) {cnt[v] += cnt[u];}}}
}
double E[N];
int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= m; i++) {int u, v;scanf("%d%d", &u, &v);add_edge(u, v);}int k;scanf("%d", &k);while (k--) {int a, b;scanf("%d%d", &a, &b);bfs(a, b, cnt[0], d[0]); //起点到终点bfs(b, a, cnt[1], d[1]); //终点到起点if (d[0][b] == INF) continue;for (int i = 0; i < n; i++) {if (d[0][i] + d[1][i] == d[0][b])E[i] += 1.0 * cnt[0][i] * cnt[1][i] / cnt[0][b];}}double mmax = -INF;int pos = -1;for (int i = 0; i < n; i++) {if (E[i] > mmax) {mmax = E[i];pos = i;}}printf("%d\n", pos);return 0;
}

C-今我来思(sì)

题意: 原本有一个 000 到 n−1n-1n−1 的全排列,但是现在你不知道,你可以多次询问一段区间的最小值,判断是否有符合这样条件的排列,若有多种,只需要输出一种即可。

思路:

一开始我想用线段树来得到所有位置的下界,然后先按下界从大到小排序,将每一段下界相同的位置选一个置为该下界,然后从大到小将剩下的数填入空处。最后判断一下每一个数是否符合下界。然鹅,这个思路只过了两个点,痛苦面具 应该是后面的构造有问题。

现在来讲一个正确的解法:先将询问中所有对某个 xxx 的限制区间进行分类,然后从大到小,若该数 xxx 无区间限制,就将它 先存起来,继续下一个数;否则,将该数 xxx 的所有区间的交集求出来(用了一下差分),在该区间找一个未确定的位置,赋为当前的数,然后区间中剩下的位置因为最小是当前的数 xxx ,一次必须要用比 xxx 大的数来赋值,因此要用到之前没有区间限制的数来赋值。若无法将交集区间全部填满,就说明不存在。

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#define fi first
#define se second
//#include<stdlib.h>
//#include <time.h>
//srand((unsigned)time(NULL));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
using namespace std;
const double eps = 1e-7;
const int N = 2e5 + 10;struct node{int l, r;node() {}node(int _l, int _r) {l = _l; r = _r;}
}a[N];
int have[N];
int top = 0;
int f[N];
vector<node> vc[N];
int main() {int n, q;scanf("%d%d", &n, &q);vector<int> ans(n, -1);vector<int> pos(n, -1);for (int i = 0; i < q; i++) {int l, r, x;scanf("%d%d%d", &l, &r, &x);vc[x].push_back(node(l, r));}bool ff = true;for (int i = n - 1; i >= 0 && ff; i--) {if (vc[i].size() == 0) { //没有对i的限制have[++top] = i;continue;}
//        for (int j = top; j > 0; j--)
//            printf("%d ", have[j]);
//        printf("\n");//用差分寻找交集int L = INF, R = -1;for (int j = 0; j < (int)vc[i].size(); j++) {node now = vc[i][j];L = min(L, now.l);R = max(R, now.r);f[now.l]++; f[now.r + 1]--;
//            cout <<  now.l << " " << now.r << "\n";}for (int j = L + 1; j <= R + 1; j++)f[j] += f[j - 1];
//        for (int j = 0; j < n; j++)
//            printf("%d ", f[j]);
//        printf("\n");int cnt = vc[i].size(); //对i限制的区间个数为cntfor (int j = L; j <= R; j++) {if (f[j] == cnt && ans[j] == -1) {ans[j] = i;pos[i] = j;break;}}//如果没有交集或者交集的位置都已经被更大的数放满了if (pos[i] == -1) {ff = false;break;}for (int j = L; j <= R && ff; j++) { //这个区间的最小值是i,必须拿>=i的数填满if (f[j] = cnt && ans[j] == -1) {if (top == 0) ff = false;else { //因为i是从n-1开始向下遍历的,所以数组内的数字一定比i大ans[j] = have[top];pos[have[top]] = j;top--;}}}//清空差分数组for (int j = L; j <= R + 1; j++)f[j] = 0;}
//    for (int i = 1; i <= top; i++)
//        printf("%d ", have[i]);
//    printf("\n");for (int i = 0; i < n && ff; i++) {if (ans[i] == -1) {if (top == 0) ff = false;else {ans[i] = have[top];pos[ans[i]] = i;top--;}}}if (ff) {for (int i = 0; i < n; i++) {printf("%d%c", ans[i], i == n - 1 ? '\n' : ' ');}}else {for (int i = 0; i < n; i++) {printf("%d%c", -1, i == n - 1 ? '\n' : ' ');}}return 0;
}

D- 雨雪霏霏

待补

EOJ 2月月赛补题相关推荐

  1. 2016广东工业大学第一次月赛补题

    比赛链接:点这里 题目: Problem B: Linux的文件权限对不对? Description 在还没给deepin做dde的arch移植之前,felixonmars迷上了Linux的命令行,因 ...

  2. 2018洛谷8月月赛第一题_U28036 Nagisa loves Tomoya

    U28036 Nagisa loves Tomoya 题目描述: 可爱的古河渚得到了一个序列.她定义一次操作为将所有的ai变为ai+a(i mod n+1) 然后她会向你进行Q次询问,每次问你x,y意 ...

  3. 2022年5月8号补题

    title: 5月8号补题 date: 2022-05-08 10:37:59 author: "胡耀文" categories: "算法" tags: &qu ...

  4. 安恒赛php_安恒11月月赛周周练writeup

    前言 11月月赛 完美错过时间,正好有周周练,基本都是一样月赛的web,记录下write up 手速要快 这题是10月月赛中的一题,直接看我上次的writeup:安恒月赛(十)web-2题writeu ...

  5. 上计会青少年算法竞赛3月月赛

    3月月赛丙组题 https://iai.sh.cn/contest/3 打鱼还是晒网 5天一个周期,如果 n 除以5的余数是4或者0,就是晒网:否则就是打鱼 注意:任何正整数除以 r 的余数的范围是 ...

  6. csu-2018年11月月赛Round2-div1题解

    csu-2018年11月月赛Round2-div1题解 A(2191):Wells的积木游戏 Description Wells有一堆N个积木,标号1~N,每个标号只出现一次 由于Wells是手残党, ...

  7. 关于517编程的11月月赛

    关于517编程的11月月赛-Nov.29 by Jasonxu 我是传送门 T1:umin之和 1.1题目 小海狸定义 umin为最小的没有在子集中出现过的非负整数. 小海狸有一组非负整数,他希望将这 ...

  8. 2018-2019赛季多校联合新生训练赛第五场补题与题解(中石油)

    总结:这场比赛比的我很迷啊,刚开始一个多小时就a了七道题,然后往后怎么做都做不出来题了...我也不知道为什么,反正比赛途中因为一个题做不出来直接自闭(疯狂锤头),通过这场比赛我发现一件事情:打比赛的时 ...

  9. 一个算法笨蛋的12月leetCode刷题日记

    类似文章 一个算法笨蛋的2021年11月leetCode刷题日记 一个算法笨蛋的2021年12月leetCode刷题日记 一个算法笨蛋的2022年1月leetCode刷题日记 一个算法笨蛋的2022年 ...

  10. 【LeetCode】2022 7月 每日一题

    [LeetCode]2022 7月 每日一题 前言 七月太忙了,又是项目又是练车又是各种比赛.大概有10天的每日一题没有当天写完(虽然后面补上了). 将每日一题的所有思路记录在这里分享一下. 7.1 ...

最新文章

  1. Python 键盘鼠标监听
  2. [转载] 中华典故故事(孙刚)——08 狗咬吕洞宾
  3. 解决百度网盘(百度云)分享链接不存在失效、分享的文件已经被取消的问题
  4. GitHub标星14k:超详细的人工智能专家路线图
  5. LeetCode 1379. 找出克隆二叉树中的相同节点(二叉树遍历)
  6. asp.net之动态页面和静态页面的区别
  7. MySQL高可用--MGR入门(1)单主/多主模式搭建
  8. 蓝桥杯单片机:11届决赛
  9. 买了小区一楼的感受是怎样的?
  10. dmol3给定关键字不在字典中_Materials Studio自学系列——软件安装常见问题及解决方法...
  11. layui 自定义request_layuiAdmin pro v1.x 【单页版】开发者文档
  12. 转载[UGUI]深入理解Canvas Scaler
  13. 百度、太一云、趣链、星合、达令共议什么是真区块链【区块链打假】
  14. Web服务器群集——LVS-DR+Keepalived高可用集群
  15. 计算机二级前两周,知道这些,计算机二级两周够了
  16. 五轴数控转台_什么是五轴联动数控机床
  17. java实现简单的LUR算法
  18. php国际青少年书画大赛,【水墨童心】第五届 国际青少年书画大赛征稿通知
  19. 嵌入式培训c语言编程,嵌入式C语言学习秘诀
  20. 机器学习/推荐系统/推荐系统算法工程师面试指导

热门文章

  1. iOS Xcode:No account for team 5P2U9V6DNN.
  2. 随想录:开发一流Android SDK
  3. HCNE复习参考(上)
  4. OSChina 周四乱弹 ——小小编辑教你装逼斗气
  5. 世界三大顶级音响_世界十大顶级音响排行榜前十名
  6. Linux权限设置方法
  7. STM32CubeMX | STM32F1系列HAL库读写内部FLASH
  8. 怎么彻底删除users下的文件夹_users中的那些文件可以删除。。。。。。。有哪些文件是不能删除呢?...
  9. 《VoIP技术构架(第2版·修订版)》一1.4 语音与数据网合二为一的驱动力
  10. ipad如何与计算机连接网络连接不上,苹果平板电脑网络连接不可用怎么办