「SCOI2016」背单词

题意

这出题人语文水平真低。。 搬了 skylee 大佬的题意。

给你 \(n\) 个字符串,不同的排列有不同的代价,代价按照如下方式计算(字符串 \(s\) 的位置为 \(x\) ):

  1. 排在 \(s\) 后面的字符串有 \(s\) 的后缀,则代价为 \(n^2\) ;
  2. 排在 \(s\) 前面的字符串有 \(s\) 的后缀,且没有排在 \(s\) 后面的 \(s\) 的后缀,则代价为 \(x-y\)( \(y\) 为最后一个与 \(s\) 不相等的后缀的位置);
  3. \(s\) 没有后缀,则代价为 \(x\) 。

现在需要最小化排列的代价。

\(n \le 10^5, 1 \le \sum_s |s| \le 5.1 \times 10^5\)

题解

首先 \(n^2\) 的代价明显比其他两种操作的总和还多,显然不够优秀。

我们需要尽量避免 \(1\) 情况的出现,这显然是可以达到的。

因为所有后缀的出现会存在一个偏序结构,构成了一个 \(DAG\) ,我们找个 \(DFS\) 序遍历就行了。

其实这个 \(DAG\) 就是它反串构成的 \(Trie\) 。

我们相当于要在 \(Trie\) 上找一个 \(DFS\) 序把每个 关键点 编号,最小化 每个点与它祖先第一个 关键点 的编号差值的和。

关键点: 就是插入串最后的结束节点。

由于是个 \(DFS\) 序, 对于一个点的每个儿子需要遍历的话,肯定要遍历这个儿子的子树。

所以对于当前这层贪心的话,不难发现这是一个类似于接水问题,我们肯定是让需要时间较短 (\(Size\) 较小)的儿子排前面。

然后发现直接实现只会有 \(40\) 分。

为什么错了呢?

假设对于 \(\overline{ba}, \overline{bc}\) 这两个串,但是不存 \(\overline{b}\) 串。但由于 \(Trie\) 上会存在 \(\overline{b}\) 这个节点,所以我们会在空串的时候考虑的时候会假设有 \(\overline{b}\) 这个串。

这显然是不行的。其中一种解决办法就是,前面我们需要把 \(n\) 个串的前缀关系,重新建个新树。然后再按前面那样做就可以了。

所以复杂度就是 \(O(n \log n + |S|)\) 的。

代码

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define pb push_back using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} void File() {
#ifdef zjp_shadow
freopen ("2012.in", "r", stdin);
freopen ("2012.out", "w", stdout);
#endif
} typedef long long ll; ll ans = 0; namespace Trie { const int Maxn = 510010, Alpha = 26; int from[Maxn]; int Size = 0, sz[Maxn], trans[Maxn][Alpha], key[Maxn]; inline void Insert(char *str) {
int u = 0;
For (i, 1, strlen(str + 1)) {
int id = str[i] - 'a';
if (!trans[u][id]) trans[u][id] = ++ Size;
++ sz[u = trans[u][id]];
from[u] = id;
}
key[u] = true;
} vector<int> G[Maxn];
void Get_Tree(int u = 0, int Last = 0) {
if (key[u]) G[Last].pb(u), Last = u;
For (i, 0, Alpha - 1) if (trans[u][i])
Get_Tree(trans[u][i], Last);
} int id = 0;
void Dfs(int u = 0, int Last = 0) {
if (u) ans += (++ id) - Last, Last = id;
sort(G[u].begin(), G[u].end(), [&](const int &lhs, const int &rhs) { return sz[lhs] < sz[rhs]; } );
for (int v : G[u]) Dfs(v, Last);
} }; char str[510010]; int main () { File(); using namespace Trie; int n = read(); For (i, 1, n) {
scanf ("%s", str + 1);
reverse(str + 1, str + strlen(str + 1) + 1); Insert(str);
} Get_Tree(); Dfs();
printf ("%lld\n", ans); return 0; }

「SCOI2016」幸运数字

题意

有一颗 \(n\) 个点的树,每个点有个权值 \(G_i\) 。

有 \(q\) 次询问,每次询问树上一条路径上所有数 \(G_i\) 中选择一些数异或的最大值。

\(n \le 20000, q \le 200000, G_i \le 2^{60}\)

题解

从一个集合中选择一些数异或出最大值显然是线性基的模板。

那么我们只需要得到树上一条路径的线性基。

但线性基是不可减的,我们可以考虑用倍增把线性基合并。

复杂度就是 \(O((n + q) \log n \times 60^2)\) 可以卡过去。

其实有更好的解决方式,由于 \(q\) 比较大,我们尽量使它不要带 \(\log\) 。

可以点分治,把每个询问挂在被第一次被分开的中心,然后走到一个询问的点后把它线性基保留起来。

最后复杂度是 \(O((n \log n + q) \times 60^2)\) 的。

总结

树上查链不可减但可以合并的信息,有三种方式。不支持修改可以用 倍增(可在线 or 点分治(离线) or 树剖(可在线)

支持修改就用 树剖 + 线段树

注意线性基插入一个线性基的复杂度是 \(\displaystyle O(\frac{L^2}{\omega})\) 的,合并的复杂度是 \(\displaystyle O(\frac{L^3}{\omega})\) 。

代码

最后那个做法还是比较好写的。

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define pb push_back using namespace std; template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;} template<typename T>
inline T read() {
T x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} void File() {
#ifdef zjp_shadow
freopen ("2013.in", "r", stdin);
freopen ("2013.out", "w", stdout);
#endif
} typedef long long ll; template<int Maxn>
struct Linear_Base { ll Base[Maxn + 1]; Linear_Base() { Set(Base, 0); } inline void Insert(ll val) {
Fordown (i, Maxn, 0)
if (val >> i & 1) {
if (!Base[i]) { Base[i] = val; break ; }
else val ^= Base[i];
}
} inline ll Max() {
ll res = 0;
Fordown (i, Maxn, 0)
chkmax(res, res ^ Base[i]);
return res;
} }; typedef Linear_Base<60> Info; Info Merge(Info x, Info y) {
For (i, 0, 60) if (y.Base[i]) x.Insert(y.Base[i]); return x;
} void Add(Info &x, Info y) {
For (i, 0, 60) if (y.Base[i]) x.Insert(y.Base[i]);
} const int N = 20100; vector<int> G[N]; int n, q, dep[N], anc[N][17], Log2[N]; Info data[N][17]; inline ll Calc(int x, int y) {
Info res = Merge(data[x][0], data[y][0]);
if (dep[x] < dep[y]) swap(x, y);
int gap = dep[x] - dep[y];
Fordown (i, Log2[gap], 0)
if (gap >> i & 1)
Add(res, data[x][i]), x = anc[x][i]; Add(res, data[x][0]);
if (x == y)
return Merge(res, data[y][0]).Max(); Fordown (i, Log2[dep[x]], 0)
if (anc[x][i] != anc[y][i]) {
Add(res, data[x][i]);
Add(res, data[y][i]);
x = anc[x][i]; y = anc[y][i];
}
Add(res, data[x][0]);
Add(res, data[y][0]);
return Merge(res, data[anc[x][0]][0]).Max();
} void Dfs_Init(int u, int fa = 0) {
dep[u] = dep[anc[u][0] = fa] + 1;
for (int v : G[u])
if (v != fa) Dfs_Init(v, u);
} int main() { File(); n = read<int>(); q = read<int>(); For (i, 1, n) data[i][0].Insert(read<ll>());
For (i, 1, n - 1) {
int u = read<int>(), v = read<int>();
G[u].pb(v); G[v].pb(u);
}
Dfs_Init(1); For (i, 2, n) Log2[i] = Log2[i >> 1] + 1;
For (j, 1, Log2[n]) For (i, 1, n) {
anc[i][j] = anc[anc[i][j - 1]][j - 1];
data[i][j] = Merge(data[i][j - 1], data[anc[i][j - 1]][j - 1]);
} while (q --)
printf ("%lld\n", Calc(read<int>(), read<int>())); return 0; }

「SCOI2016」萌萌哒

题意

一个长度为 $ n $ 的大数,用 $ S_1S_2S_3 \ldots S_n $表示,其中 $ S_i $ 表示数的第 $ i $ 位,$ S_1 $ 是数的最高位,告诉你一些限制条件,每个条件表示为四个数 $ (l_1, r_1, l_2, r_2) $,即两个长度相同的区间,表示子串 $ S_{l_1}S_{l_1 + 1}S_{l_1 + 2} \ldots S_{r_1} $ 与 $ S_{l_2}S_{l_2 + 1}S_{l_2 + 2} \ldots S_{r_2} $ 完全相同。

比如 $ n = 6 $ 时,某限制条件 $ (l_1 = 1, r_1 = 3, l_2 = 4, r_2 = 6) $,那么 \(123123\) 、\(351351\) 均满足条件,但是 \(12012\)、\(131141\) 不满足条件,前者数的长度不为 $ 6 $,后者第二位与第五位不同。问满足以上所有条件的数有多少个。

$ 1 \leq n \leq 10 ^ 5, 1 \leq m \leq 10 ^ 5, 1 \leq {l_i}_1, {r_i}_1, {l_i}_2, {r_i}_2 \leq n $ 并且保证 $ {r_i}_1 - {l_i}_1 = {r_i}_2 - {l_i}_2 $。

题解

不难发现其实每次就是限制对应的位置数相同。

然后最后就会限制成 \(tot\) 种可以填的不同的数,最后的答案就是 \(9 \times 10^{tot - 1}\) 。

我们把强制要求相等的数放入并查集中就行了。直接实现这个过程是 \(O(n^2 \alpha(n))\) 的复杂度。

考虑如何优化,区间对应连边容易想到线段树优化建边。但此处没有那么好用。

考虑把一个区间拆成 \(\log\) 个长为 \(2^i\) 的区间。

具体来说令 \(fa[i][j]\) 为 \(i\) 向右长为 \(2^j\) 的区间在并查集上的 \(root\) 。

初始化的时候 \(fa[i][j]\) 的 \(root\) 为 \(i\) 。

然后一个询问我们就在对应区间上连边就行了。

但是最后我们区间的连通性并不能代表点的连通性,所以我们需要把连通性下放。

具体来说把 \((i, j)\) 这个点和 \((i, j - 1), (i + 2^{j - 1}, j - 1)\) 对应相连就行了。

最后看 \((i, 0)\) 有几个不同的联通块就可以完成了。

其实前面那个那个不需要拆成 \(O(\log n)\) 个区间。由于是要保证联通,多连是没有关系的,我们可以类似 \(ST\) 表查询那样,把最大的两个可以代表区间相连。

复杂度就是 \(O((n \log n + m) \alpha (n))\) 。

总结

这题还是比较巧妙的,类似于线段树优化建边的思想,我们可以拆成子区间去解决这个问题。

类似于这种题,只要构建出模型保证和原图一样的连通性即可。

代码

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} void File() {
#ifdef zjp_shadow
freopen ("2014.in", "r", stdin);
freopen ("2014.out", "w", stdout);
#endif
} const int N = 2e5 + 1e3; int n, m, fa[N][22], Log2[N]; int find(int x, int y) {
return fa[x][y] == x ? x : fa[x][y] = find(fa[x][y], y);
} const int Mod = 1e9 + 7; inline int fpm(int x, int power) {
int res = 1;
for (; power; power >>= 1, x = 1ll * x * x % Mod)
if (power & 1) res = 1ll * res * x % Mod;
return res;
} int main () { File(); n = read(); m = read(); For (i, 2, n) Log2[i] = Log2[i >> 1] + 1;
For (i, 1, n) For (j, 0, Log2[n]) fa[i][j] = i; For (i, 1, m) {
int p1 = read(), r1 = read(), p2 = read(), r2 = read();
int len = Log2[r1 - p1 + 1];
fa[find(p1, len)][len] = find(p2, len);
fa[find(r1 - (1 << len) + 1, len)][len] = find(r2 - (1 << len) + 1, len);
} Fordown (j, Log2[n], 1) For (i, 1, n) {
int u = find(i, j), v = find(i, j - 1);
fa[v][j - 1] = find(u, j - 1);
u = find(i, j), v = find(i + (1 << j - 1), j - 1);
fa[v][j - 1] = find(u + (1 << j - 1), j - 1);
} int tot = 0;
For (i, 1, n) if(find(i, 0) == i) ++ tot;
printf ("%lld\n", fpm(10, tot - 1) * 9ll % Mod); return 0; }

SCOI2016 Day1 简要题解的更多相关文章

  1. A · F · O —— JLOI2018翻车记(附Day1简要题解)

    JLOI2018翻车记 并不知道该怎么写... 算了还是按照标准剧情来吧 这应该是一篇写得非常差的流水账... 2018.04.04 Day -1 省选前在机房的最后一天. 压力并不是很大,毕竟联赛 ...

  2. SCOI2016 Day2 简要题解

    「SCOI2016」妖怪 题意 有 \(n\) 只妖怪,每只妖怪有攻击力 \(\text{atk}\) 和防御力 \(\text{dnf}\) ,在环境 \((a, b)\) 下,它可以把攻击力和防御 ...

  3. JLOI2015 DAY1 简要题解

    「JLOI2015」有意义的字符串 题意 给你 \(b, d, n\) 求 \[ [(\frac{b + \sqrt d}2)^n] \mod 7528443412579576937 \] \(0 & ...

  4. SCOI 2015 Day1 简要题解

    「SCOI2015」小凸玩矩阵 题意 一个 \(N \times M\)( $ N \leq M $ )的矩阵 $ A $,要求小凸从其中选出 $ N $ 个数,其中任意两个数字不能在同一行或同一列, ...

  5. [NOIP 2018 Day1] 简要题解

    [题目链接] 铺设道路 : https://www.luogu.org/problemnew/show/P5019 货币系统 : https://www.luogu.org/problemnew/sh ...

  6. AHOI2013 Round2 Day1 简要题解

    第一题,好吧这是个dp.(搜素也能在BZOJ上卡过). 第二题,BFS搜索碰到的立方体面数,智硬没有想到... 第三题,其实一看就有思路,但关键是求x坐标不交的矩形对数+y坐标不交的矩形对数 - x, ...

  7. Noip 2014酱油记+简要题解

    好吧,day2T1把d默认为1也是醉了,现在只能期待数据弱然后怒卡一等线吧QAQ Day0 第一次下午出发啊真是不错,才2小时左右就到了233,在车上把sao和fate补掉就到了= = 然后到宾馆之后 ...

  8. Tsinghua 2018 DSA PA2简要题解

    反正没时间写,先把简要题解(嘴巴A题)都给他写了记录一下. upd:任务倒是完成了,我也自闭了. CST2018 2-1 Meteorites: 乘法版的石子合并,堆 + 高精度. 写起来有点烦貌似. ...

  9. Codeforces 863 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 简要题解?因为最后一题太毒不想写了所以其实是部分题解... A题 传送门 题意简述:给你一个数,问你能不能通过加前导000使其成为一个回文数 ...

随机推荐

  1. 面象对象设计原则之七:合成复用原则(Composition/Aggregate Reuse Principle, CARP)

    合成复用原则又称为组合/聚合复用原则(Composition/Aggregate Reuse Principle, CARP),其定义如下: 合成复用原则(Composite Reuse Princi ...

  2. MySQL — 优化之explain执行计划详解(转)

    EXPLAIN简介 EXPLAIN 命令是查看查询优化器如何决定执行查询的主要方法,使用EXPLAIN,只需要在查询中的SELECT关键字之前增加EXPLAIN这个词即可,MYSQL会在查询上设置一个 ...

  3. Java 里如何实现线程间通信(转载)

    出处:http://www.importnew.com/26850.html 正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程 ...

  4. day 7-11 初识MySQL数据库及安装密码设置破解

    一. 什么是数据库 之前所学,数据要永久保存,比如用户注册的用户信息,都是保存于文件中,而文件只能存在于某一台机器上. 如果我们不考虑从文件中读取数据的效率问题,并且假设我们的程序所有的组件都运行在一 ...

  5. MCV 和 MTV框架基本信息

    一 . MCV # web服务器开发最著名的MVC模式 M : model.py 就是和数据库打交道的, 创建表等操作 V : view 视图(视图函数,就是装HTML文件的) C : control ...

  6. python之路--装饰器

    二 .通用装饰器的写法 python里面的动态代理. 存在的意义: 在不破坏原有的函数和原有函数的调用基础上,给函数添加新的功能 def wrapper(fn): # fn是目标函数. def inn ...

  7. 在linux系统中实现各项监控的关键技术(2)--内核态与用户态进程之间的通信netlink

    Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来使用 ...

  8. python学习笔记(8)--random库的使用

    伪随机数:采用梅森旋转算法生成的伪随机序列中元素 使用random库 一.基本随机函数 随机数需要一个种子,依据这个种子通过梅森旋转算法产生固定序列的随机数.seed(a=None)  初始化给定的随 ...

  9. ASP.NET Web.config文件的配置(Configuration API)

    本次我们讨论主要聚焦在以下Web.config配置文件的设置值的读取. 1.<connectionString />连接字符串的读取. 2.<appSettings />应用程 ...

  10. 21.PHP实现Word/Excel/PPT转换为PDF

    参考文档: https://www.cnblogs.com/woider/p/7003481.html http://blog.csdn.net/aoshilang2249/article/detai ...