21.P4172 [WC2006]水管局长

SC 省 MY 市有着庞大的地下水管网络,嘟嘟是 MY 市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从 \(x\) 处送往 \(y\) 处,嘟嘟需要为供水公司找到一条从 \(A\) 至 \(B\) 的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了。嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项。

在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗、消毒等等。嘟嘟在控制中心一声令下,这些水管的准备操作同时开始,但由于各条管道的长度、内径不同,进行准备操作需要的时间可能不同。供水公司总是希望嘟嘟能找到这样一条送水路径,路径上的所有管道全都准备就绪所需要的时间尽量短。嘟嘟希望你能帮助他完成这样的一个选择路径的系统,以满足供水公司的要求。另外,由于 MY 市的水管年代久远,一些水管会不时出现故障导致不能使用,你的程序必须考虑到这一点。

不妨将 MY 市的水管网络看作一幅简单无向图(即没有自环或重边):水管是图中的边,水管的连接处为图中的结点。


这道题只有删边操作,我们可以把这个过程看做是加边操作,这样好处理一点。

题目保证了图无论怎么删边都保证联通,所以我们可以先把图删完。删完后我们呢求出图的MST, 那么任意两点在这颗MST上走都是最优的别问我怎么证贪心是用来证的吗。我们可以用LCT来维护这颗MST,Splay维护区间最大值(也就是链上的最大值)

考虑加边操作。上面我提到了一个贪心:任意两点在这颗MST上走都是最优的。如果此时我们在MST上任意加一条边都会形成一个环。我们可以从任意一点入环,任意一点出环,方向随意。可以发现我们的最优解始终能避开最长的一条边!

考察这个环,用lct的split操作提出的信息,查出最大的权值。发现我们可以通过删除权值最大的边来保证上的最优。

怎么求出这条边呢?显然我们可以二分来找

再来考虑一个问题:如何维护边呢?

考虑用一个点来表示一条边。

最后逆序加边,把答案逆序输出即可

(良心题解)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
#define mid ((l + r) >> 1)
#define mp make_pair
#define fir first
#define sec second
#define pub push_back
#define pob pop_back using namespace std;
typedef long long LL; #define io_e '\0'
#define io_s ' '
#define io_l '\n'
#define _DEBUG_ 1 // debug toggle
namespace Fast_IO {
#ifndef _DEBUG_
#define gc() (iS == iT ? (iT = (iS = ibuff) + fread(ibuff, 1, SIZ, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
#else
#define gc() getchar()
#endif
const int SIZ = 1 << 21 | 1;
char *iS, *iT, ibuff[SIZ], obuff[SIZ], *oS = obuff, *oT = oS + SIZ - 1, fu[110], c;
int fr;
inline void ioout() {
fwrite(obuff, 1, oS - obuff, stdout);
oS = obuff;
}
template <class Type>
inline void read(Type& x) {
x = 0;
Type y = 1;
for (c = gc(); (c > '9' || c < '0') && c ^ '-'; c = gc())
;
c == '-' ? y = -1 : x = (c & 15);
for (c = gc(); c >= '0' && c <= '9'; c = gc()) x = x * 10 + (c & 15);
x *= y;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
inline void read(char* s) {
register char ch = gc();
for (; blank(ch); ch = gc())
;
for (; !blank(ch); ch = gc()) *s++ = ch;
*s = 0;
}
inline void read(char& c) {
for (c = gc(); blank(c); c = gc())
;
}
template <typename Type, typename... Args>
inline void read(Type& t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char* t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char& t, Args&... args) {
read(t), read(args...);
}
template <class Type>
inline void write(char lastChar, Type x) {
if (x < 0)
*oS++ = '-', x = -x;
if (x == 0)
*oS++ = '0';
while (x) fu[++fr] = x % 10 + '0', x /= 10;
while (fr) *oS++ = fu[fr--];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x[]) {
for (register int i = 0; x[i]; ++i) *oS++ = x[i];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x) {
*oS++ = x;
*oS++ = lastChar;
ioout();
}
template <typename Type, typename... Args>
inline void write(char midChar, Type t, Args... args) {
write(midChar, t), write(midChar, args...);
}
} // namespace Fast_IO using Fast_IO::read;
using Fast_IO::write; namespace LinkCutTree {
const int SIZE = 12e4 + 5;
struct SPLAY {
int ch[2];
int fa;
int key;
int maxValue;
int lazyTag;
} T[SIZE];
stack < int > MemoryWaste;
#define ls T[x].ch[0]
#define rs T[x].ch[1]
#define WhichSon(x) (T[T[x].fa].ch[1] == x)
#define IsRoot(x) (T[T[x].fa].ch[0] ^ x && T[T[x].fa].ch[1] ^ x) void UpdateMessage(int x) {
T[x].maxValue = max(max(T[ls].maxValue, T[x].key), T[rs].maxValue);
} void UpdateSons(int x) {
if (T[x].lazyTag) {
ls ^= rs ^= ls ^= rs;
T[x].lazyTag = 0;
T[ls].lazyTag ^= 1;
T[rs].lazyTag ^= 1;
}
} void RotateNode(int x) {
int y = T[x].fa;
if (!IsRoot(y)) T[T[y].fa].ch[WhichSon(y)] = x;
bool k = WhichSon(x);
T[x].fa = T[y].fa;
T[y].fa = x;
T[y].ch[k] = T[x].ch[k ^ 1];
T[T[y].ch[k]].fa = y;
T[x].ch[k ^ 1] = y;
UpdateMessage(y);
UpdateMessage(x);
} void LinkSplay(int x) {
int u = x;
while (!IsRoot(u)) MemoryWaste.push(u), u = T[u].fa;
MemoryWaste.push(u);
while (MemoryWaste.size()) UpdateSons(MemoryWaste.top()), MemoryWaste.pop();
for (; !IsRoot(x); RotateNode(x)) {
int y = T[x].fa;
if (!IsRoot(y))
RotateNode(WhichSon(x) ^ WhichSon(y) ? x : y);
}
}
void AccessEdge(int x) {
for (int u = x, y = 0; u; y = u, u = T[u].fa) {
LinkSplay(u);
T[u].ch[1] = y;
UpdateMessage(u);
}
} void MakeRoot(int x) {
AccessEdge(x);
LinkSplay(x);
T[x].lazyTag ^= 1;
} void SplitTree(int x, int y) {
MakeRoot(x);
AccessEdge(y);
LinkSplay(y);
} void LinkTree(int x, int y) {
MakeRoot(x);
T[x].fa = y;
} void CutTree(int x, int y) {
MakeRoot(x);
AccessEdge(y);
LinkSplay(y);
T[x].fa = T[y].ch[0] = 0;
} int FindByKey(int x, int u) {
if (T[x].key == u) return x;
else if (T[ls].maxValue == u) return FindByKey(ls, u);
else return FindByKey(rs, u);
}
} // namespace LinkCutTree using namespace LinkCutTree; int F[1005][1005], U[101000], V[101000];
int OP[101000], ans[101000], n, m, QueryNumber;
struct EdgeNode {
int x, y;
int val, key;
EdgeNode() { key = 1; }
friend bool operator < (EdgeNode X, EdgeNode Y) {
return X.val < Y.val;
}
} e[101000];
struct UnionFindSet {
int fa[1010]; int find(int x) {
if (x ^ fa[x]) fa[x] = find(fa[x]);
return fa[x];
} void merge(int x, int y) {
int u = find(x), v = find(y);
if (u ^ v) fa[u] = v;
} void init(int n, int m) {
for (int i = 1; i <= n; ++i)
fa[i] = i;
for (int i = 1; i <= m; ++i) {
if (e[i].key && find(e[i].x) ^ find(e[i].y)) {
merge(e[i].x, e[i].y);
LinkTree(e[i].x, n + i);
LinkTree(e[i].y, n + i);
}
}
}
} ufs; signed main() {
read(n, m, QueryNumber);
for (int i = 1; i <= m; ++i)
read(e[i].x, e[i].y, e[i].val);
sort(e + 1, e + 1 + m);
for (int i = 1; i <= m; ++i) {
F[e[i].x][e[i].y] = i;
F[e[i].y][e[i].x] = i;
T[n + i].key = e[i].val;
}
for (int i = 1; i <= QueryNumber; ++i) {
read(OP[i], U[i], V[i]);
if (OP[i] == 2) e[F[U[i]][V[i]]].key = 0;
}
ufs.init(n, m);
int EdgeCount = 0;
for (int i = QueryNumber; i >= 1; --i) {
SplitTree(U[i], V[i]);
if (OP[i] == 1) ans[++EdgeCount] = T[V[i]].maxValue;
else {
int Temporary = FindByKey(V[i], T[V[i]].maxValue);
if (T[F[U[i]][V[i]] + n].key < T[V[i]].maxValue) {
CutTree(e[Temporary - n].x, Temporary);
CutTree(e[Temporary - n].y, Temporary);
LinkTree(U[i], F[U[i]][V[i]] + n);
LinkTree(V[i], F[U[i]][V[i]] + n);
}
}
}
for (int i = EdgeCount; i >= 1; --i)
write(io_l, ans[i]);
return 0;
}

22.P3302 [SDOI2013]森林

小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有M条边。

小Z希望执行T个操作,操作有两类:

  1. Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少。此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点。
  2. L x y在点x和点y之间连接一条边。保证完成此操作后,仍然是一片森林。

为了体现程序的在线性,我们把输入数据进行了加密。设lastans为程序上一次输出的结果,初始的时候lastans为0。

  • 对于一个输入的操作Q x y k,其真实操作为Q x^lastans y^lastans k^lastans
  • 对于一个输入的操作L x y,其真实操作为L x^lastans y^lastans。其中^运算符表示异或,等价于pascal中的xor运算符。

请写一个程序來帮助小Z完成这些操作。


查询操作显然可以用主席树来完成,然而连接树的操作又让我们想到了lct。怎么办呢?主席树+LCT?(据说还真有人这么干)

启发式合并!

没错,我们用选择用主席树来完成这道题,合并时采用启发式合并。

具体来说就是每次合并时都用大小较小的树往大的合并,然后暴力遍历大小较小的树更新倍增数组和主席树即可

#pragma GCC diagnostic error "-std=c++11"
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define _size_ (tr[tr[x].l].size + tr[tr[y].l].size - tr[tr[lca].l].size - tr[tr[fa_lca].l].size)
#define pii pair < int , int >
#define pll pair < LL, LL >
#define mid ((l + r) >> 1)
#define mp make_pair
#define fir first
#define sec second
#define pub push_back
#define pob pop_back using namespace std;
typedef long long LL; #define io_e '\0'
#define io_s ' '
#define io_l '\n'
#define _DEBUG_ 1 // debug toggle
namespace Fast_IO {
#ifndef _DEBUG_
#define gc() (iS == iT ? (iT = (iS = ibuff) + fread(ibuff, 1, SIZ, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
#else
#define gc() getchar()
#endif
const int SIZ = 1 << 21 | 1;
char *iS, *iT, ibuff[SIZ], obuff[SIZ], *oS = obuff, *oT = oS + SIZ - 1, fu[110], c;
int fr;
inline void ioout() {
fwrite(obuff, 1, oS - obuff, stdout);
oS = obuff;
}
template <class Type>
inline void read(Type& x) {
x = 0;
Type y = 1;
for (c = gc(); (c > '9' || c < '0') && c ^ '-'; c = gc())
;
c == '-' ? y = -1 : x = (c & 15);
for (c = gc(); c >= '0' && c <= '9'; c = gc()) x = x * 10 + (c & 15);
x *= y;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
inline void read(char* s) {
register char ch = gc();
for (; blank(ch); ch = gc())
;
for (; !blank(ch); ch = gc()) *s++ = ch;
*s = 0;
}
inline void read(char& c) {
for (c = gc(); blank(c); c = gc())
;
}
template <typename Type, typename... Args>
inline void read(Type& t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char* t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char& t, Args&... args) {
read(t), read(args...);
}
template <class Type>
inline void write(char lastChar, Type x) {
if (x < 0)
*oS++ = '-', x = -x;
if (x == 0)
*oS++ = '0';
while (x) fu[++fr] = x % 10 + '0', x /= 10;
while (fr) *oS++ = fu[fr--];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x[]) {
for (register int i = 0; x[i]; ++i) *oS++ = x[i];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x) {
*oS++ = x;
*oS++ = lastChar;
ioout();
}
template <typename Type, typename... Args>
inline void write(char midChar, Type t, Args... args) {
write(midChar, t), write(midChar, args...);
}
} // namespace Fast_IO using Fast_IO::read;
using Fast_IO::write; const int SIZE = 9e4 + 5;
const int LSIZE = SIZE << 7;
const int GSIZE = SIZE << 1;
int n, m, q, waste;
int tot, rt[LSIZE], b[SIZE];
int pri_n, edge_tot, a[SIZE];
int Head[GSIZE], Next[GSIZE];
int Vertex[GSIZE], Weight[GSIZE];
int f[SIZE][LSIZE / SIZE >> 2];
int dp[SIZE], fa[SIZE], id[SIZE];
int size[SIZE], vis[SIZE];
struct TreeNode {
int l, r;
int size;
} tr[LSIZE]; void add(int x, int y, int z = 1) {
Vertex[++edge_tot] = y, Weight[edge_tot] = z;
Next[edge_tot] = Head[x], Head[x] = edge_tot;
} int find_set(int x) {
return x == fa[x] ? x : fa[x] = find_set(fa[x]);
} int make(int l, int r) {
int u = ++tot;
if (l ^ r) return tr[u].l = make(l, mid), tr[u].r = make(mid + 1, r), u;
else return 0;
} void modify(int &u, int pre, int l, int r, int x) {
u = ++tot;
tr[u] = TreeNode{tr[pre].l, tr[pre].r, tr[pre].size + 1};
if (l ^ r)
if (mid >= x) modify(tr[u].l, tr[pre].l, l, mid, x);
else modify(tr[u].r, tr[pre].r, mid + 1, r, x);
else return ;
} int query(int x, int y, int lca, int fa_lca, int l, int r, int k) {
if (l ^ r)
if (_size_ >= k) return query(tr[x].l, tr[y].l, tr[lca].l, tr[fa_lca].l, l, mid, k);
else return query(tr[x].r, tr[y].r, tr[lca].r, tr[fa_lca].r, mid + 1, r, k - _size_);
else return l;
} void dfs(int x, int _rt_) {
for (int i = 1; i < 17; ++i) f[x][i] = f[f[x][i - 1]][i - 1];
modify(rt[x], rt[f[x][0]], 1, pri_n, id[x]);
#define y Vertex[i]
for (int i = Head[x]; i; i = Next[i]) if (y ^ fa[x]) f[y][0] = x, fa[y] = x, dp[y] = dp[x] + 1, vis[x] = true, size[_rt_]++, dfs(y, _rt_);
#undef y
} int get_lca(int x, int y) {
if (dp[x] < dp[y]) x ^= y ^= x ^= y;
for (int i = 16; i >= 0; --i) if (f[x][i] && dp[f[x][i]] >= dp[y]) x = f[x][i];
if (x == y) return x;
for (int i = 16; i >= 0; --i) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
} signed main() {
read(waste, n, m, q);
for (int i = 1; i <= n; ++i) read(a[i]), b[i] = a[i];
sort(b + 1, b + 1 + n);
pri_n = unique(b + 1, b + 1 + n) - b - 1;
for (int i = 1; i <= n; ++i) id[i] = lower_bound(b + 1, b + 1 + pri_n, a[i]) - b;
for (int i = 1, x, y; i <= m; ++i) read(x, y), add(x, y, 1), add(y, x, 1);
*rt = make(1, pri_n);
for (int i = 1; i <= n; ++i) if (!vis[i]) dfs(i, i), fa[i] = i;
int ans = 0, lastans = 0;
for (int enum_q = 0; enum_q < q; ++enum_q) {
char opt[5];
int x, y, k, lca;
read(opt), read(x, y);
x ^= lastans, y ^= lastans;
if (*opt == 'Q') read(k), k ^= lastans, lca = get_lca(x, y), write(io_l, lastans = ans = b[query(rt[x], rt[y], rt[lca], rt[f[lca][0]], 1, pri_n, k)]);
else {
add(x, y, 1);
add(y, x, 1);
int u = find_set(x);
int v = find_set(y);
if (size[u] < size[v]) x ^= y ^= x ^= y, u ^= v ^= u ^= v;
f[y][0] = x;
fa[y] = x;
dp[y] = dp[x] + 1;
vis[y] = true;
size[u]++;
dfs(y, v);
}
}
return 0;
}

23.P3250 [HNOI2016]网络

一个简单的网络系统可以被描述成一棵无根树。每个节点为一个服务器。连接服务器与服务器的数据线则看做一条树边。两个服务器进行数据的交互时,数据会经过连接这两个服务器的路径上的所有服务器(包括这两个服务器自身)。

由于这条路径是唯一的,当路径上的某个服务器出现故障,无法正常运行时,数据便无法交互。此外,每个数据交互请求都有一个重要度,越重要的请求显然需要得到越高的优先处理权。现在,你作为一个网络系统的管理员,要监控整个系统的运行状态。系统的运行也是很简单的,在每一个时刻,只有可能出现下列三种事件中的一种:

  1. 在某两个服务器之间出现一条新的数据交互请求;

  2. 某个数据交互结束请求;

  3. 某个服务器出现故障。系统会在任何故障发生后立即修复。也就是在出现故障的时刻之后,这个服务器依然是正常的。但在服务器产生故障时依然会对需要经过该服务器的数据交互请求造成影响。

你的任务是在每次出现故障时,维护未被影响的请求中重要度的最大值。注意,如果一个数据交互请求已经结束,则不将其纳入未被影响的请求范围。


提供 \(\Theta(n\log^3n)\) 的暴力树剖打法。

本来我想搞树套树+树剖的,但发现Splay常数过大容易T爆,fhq-treap也炸了。

后来我幡然醒悟我们只需要开两个优先队列一个放添加的值,一个放删除的值。

碰到查询操作时先比较两个堆的堆顶,如果相同显然它被删掉了,两个栈同时弹出;不相同时添加元素队列的队首就是答案。

而且这道题开O2效果特别明显,2.x s 能变成 xxx ms!

#pragma GCC diagnostic error "-std=c++11"
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define CheckSize ((d[top[x]] < d[top[y]]) && (x ^= y ^= x ^= y))
#define ForGraph int i = Head[x], y = Vertex[i]; i; i = Next[i], y = Vertex[i]
#define pii pair < int , int >
#define pll pair < LL, LL >
#define mid ((l + r) >> 1)
#define mp make_pair
#define fir first
#define sec second
#define pub push_back
#define pob pop_back using namespace std;
typedef long long LL; #define io_e '\0'
#define io_s ' '
#define io_l '\n'
// #define _DEBUG_ 1 // debug toggle
namespace Fast_IO {
#ifndef _DEBUG_
#define gc() (iS == iT ? (iT = (iS = ibuff) + fread(ibuff, 1, SIZ, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
#else
#define gc() getchar()
#endif
const int SIZ = 1 << 21 | 1;
char *iS, *iT, ibuff[SIZ], obuff[SIZ], *oS = obuff, *oT = oS + SIZ - 1, fu[110], c;
int fr;
inline void ioout() {
fwrite(obuff, 1, oS - obuff, stdout);
oS = obuff;
}
template <class Type>
inline void read(Type& x) {
x = 0;
Type y = 1;
for (c = gc(); (c > '9' || c < '0') && c ^ '-'; c = gc())
;
c == '-' ? y = -1 : x = (c & 15);
for (c = gc(); c >= '0' && c <= '9'; c = gc()) x = x * 10 + (c & 15);
x *= y;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
inline void read(char* s) {
register char ch = gc();
for (; blank(ch); ch = gc())
;
for (; !blank(ch); ch = gc()) *s++ = ch;
*s = 0;
}
inline void read(char& c) {
for (c = gc(); blank(c); c = gc())
;
}
template <typename Type, typename... Args>
inline void read(Type& t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char* t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char& t, Args&... args) {
read(t), read(args...);
}
template <class Type>
inline void write(char lastChar, Type x) {
if (x < 0)
*oS++ = '-', x = -x;
if (x == 0)
*oS++ = '0';
while (x) fu[++fr] = x % 10 + '0', x /= 10;
while (fr) *oS++ = fu[fr--];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x[]) {
for (register int i = 0; x[i]; ++i) *oS++ = x[i];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x) {
*oS++ = x;
*oS++ = lastChar;
ioout();
}
template <typename Type, typename... Args>
inline void write(char midChar, Type t, Args... args) {
write(midChar, t), write(midChar, args...);
}
} // namespace Fast_IO using Fast_IO::read;
using Fast_IO::write; namespace HNOI2016_Network {
namespace IamJustForPlaying {
namespace IamJustForPlaying {
namespace IamJustForPlaying {
namespace IamJustForPlaying {
namespace IamJustForPlaying {
namespace IamJustForPlaying {
namespace IamJustForPlaying {
const int SIZE = 2e5 + 5;
const int GSIZE = SIZE << 1; namespace SegmentTree {
priority_queue < int > I[SIZE<<1], D[SIZE<<1];
#define ls (k << 1)
#define rs (k << 1 | 1) void DoModify(int k, int l, int r, int x, int y, int val, int opt) {
if (!(l > y || r < x))
if (l >= x && r <= y)
if (opt) I[k].push(val);
else D[k].push(val);
else DoModify(ls, l, mid, x, y, val, opt), DoModify(rs, mid + 1, r, x, y, val, opt);
} int GetAnswer(int k, int l, int r, int x) {
while (I[k].size() && D[k].size() && I[k].top() == D[k].top()) I[k].pop(), D[k].pop();
int res = I[k].size() ? I[k].top() : -1;
if (l ^ r)
if (mid >= x) res = max(res, GetAnswer(ls, l, mid, x));
else res = max(res, GetAnswer(rs, mid + 1, r, x));
return res;
}
} // namespace SegmentTree namespace TreeChainSplitting {
int tx[SIZE], ty[SIZE], tk[SIZE];
int Head[SIZE], Vertex[GSIZE];
int Next[GSIZE], EdgeCount;
int fa[SIZE], size[SIZE];
int d[SIZE], son[SIZE];
int dfn[SIZE], rnk[SIZE];
int top[SIZE], tot;
int edge_tot = 0, n, m;
struct Vector2 {
int x; int y; friend bool operator < (Vector2 rhs1, Vector2 rhs2) { return rhs1.x < rhs2.x; }
} Vec2[SIZE]; void add(int x, int y) {
Vertex[++edge_tot] = y;
Next[edge_tot] = Head[x];
Head[x] = edge_tot;
} void dfs1(int x, int fa) {
size[x] = 1, TreeChainSplitting::fa[x] = fa, d[x] = d[fa] + 1;
for (ForGraph) if (y ^ fa) dfs1(y, x), size[x] += size[y], ((size[son[x]] < size[y]) && (son[x] = y));
} void dfs2(int x, int tp) {
top[x] = tp, dfn[x] = ++tot, rnk[tot] = x;
if (son[x]) dfs2(son[x], tp);
for (ForGraph) if (y ^ fa[x] && y ^ son[x]) dfs2(y, y);
} void ModifySubTree(int x, int y, int val, int opt, int tp = 0) {
while (top[x] ^ top[y]) CheckSize, Vec2[++tp] = {dfn[top[x]], dfn[x]}, x = fa[top[x]];
if (dfn[x] > dfn[y]) x ^= y ^= x ^= y;
Vec2[++tp] = {dfn[x], dfn[y]};
sort(Vec2 + 1, Vec2 + 1 + tp);
int Sys = 1;
for (int i = 1; i <= tp; ++i) ((Sys < Vec2[i].x) && (SegmentTree::DoModify(1, 1, n, Sys, Vec2[i].x - 1, val, opt), 1)), Sys = Vec2[i].y + 1;
if (Sys <= n) SegmentTree::DoModify(1, 1, n, Sys, n, val, opt);
} void main() {
read(n, m);
for (int i = 1, x, y; i < n; ++i) read(x, y), add(x, y), add(y, x);
dfs1(1, 0), dfs2(1, 1);
for (int i = 1, x, y, k, t, opt; i <= m; ++i) {
read(opt);
if (opt == 0) read(x, y, k), ModifySubTree(x, y, k, 1), tx[i] = x, ty[i] = y, tk[i] = k;
else if (opt == 1) read(t), ModifySubTree(tx[t], ty[t], tk[t], 0);
else if (opt == 2) read(x), write(io_l, SegmentTree::GetAnswer(1, 1, n, dfn[x]));
}
}
} // namespace TreeChainSplittin
} // namespace IamJustForPlaying
} // namespace IamJustForPlaying
} // namespace IamJustForPlaying
} // namespace IamJustForPlaying
} // namespace IamJustForPlaying
} // namespace IamJustForPlaying
} // namespace IamJustForPlaying
} // namespace HNOI2016_Network signed main() {
HNOI2016_Network::IamJustForPlaying::IamJustForPlaying::IamJustForPlaying::IamJustForPlaying::IamJustForPlaying::IamJustForPlaying::IamJustForPlaying::TreeChainSplitting::main();
}

24.P4211 [LNOI2014]LCA

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。

设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。

有q次询问,每次询问给出l r z,求\(\sum_{l \leq i \leq r}dep[LCA(i,z)]\)


问题可以转化为求点到根的距离+1,询问l到r的结点到根的距离和。

这相当于是在求结点到根的点权+1的和。

那么该怎么办呢?

显然我们可以离线得到所有的询问,然后将差分询问,也就是查询 \([1,r]-[1,l-1]\)

我们可以在l-1和r处打标记,然后遍历树的结点有标记就执行查询操作。

方便起见我们可以用树链剖分来维护,时间复杂度n乘上一个Log方n。

(对了我的代码特别玄学,查询部分循环版本在luogu上T了,在lojA了,递归版本在luoguA了,在lojT了!)

(我对这份代码除了缓缓地打出一个问号也不能说什么了)

#pragma GCC diagnostic error "-std=c++11"
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define IT vector < int >::iterator
#define ForGraph int i = Head[x], y = Vert[i]; i; i = Next[i], y = Vert[i]
#define CheckSize ((size[son[x]] < size[y]) && (son[x] = y))
#define TCS TreeChainSplitting
#define CFS ChainForwardStar
#define PS ProblemSolver
#define pii pair < int , int >
#define pll pair < LL, LL >
#define mid ((l + r) >> 1)
#define mp make_pair
#define fir first
#define sec second
#define pub push_back
#define pob pop_back using namespace std;
typedef long long LL; #define io_e '\0'
#define io_s ' '
#define io_l '\n'
#define _DEBUG_ 1 // debug toggle
namespace Fast_IO {
#ifndef _DEBUG_
#define gc() (iS == iT ? (iT = (iS = ibuff) + fread(ibuff, 1, SIZ, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
#else
#define gc() getchar()
#endif
const int SIZ = 1 << 21 | 1;
char *iS, *iT, ibuff[SIZ], obuff[SIZ], *oS = obuff, *oT = oS + SIZ - 1, fu[110], c;
int fr;
inline void ioout() {
fwrite(obuff, 1, oS - obuff, stdout);
oS = obuff;
}
template <class Type>
inline void read(Type& x) {
x = 0;
Type y = 1;
for (c = gc(); (c > '9' || c < '0') && c ^ '-'; c = gc())
;
c == '-' ? y = -1 : x = (c & 15);
for (c = gc(); c >= '0' && c <= '9'; c = gc()) x = x * 10 + (c & 15);
x *= y;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
inline void read(char* s) {
register char ch = gc();
for (; blank(ch); ch = gc())
;
for (; !blank(ch); ch = gc()) *s++ = ch;
*s = 0;
}
inline void read(char& c) {
for (c = gc(); blank(c); c = gc())
;
}
template <typename Type, typename... Args>
inline void read(Type& t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char* t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char& t, Args&... args) {
read(t), read(args...);
}
template <class Type>
inline void write(char lastChar, Type x) {
if (x < 0)
*oS++ = '-', x = -x;
if (x == 0)
*oS++ = '0';
while (x) fu[++fr] = x % 10 + '0', x /= 10;
while (fr) *oS++ = fu[fr--];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x[]) {
for (register int i = 0; x[i]; ++i) *oS++ = x[i];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x) {
*oS++ = x;
*oS++ = lastChar;
ioout();
}
template <typename Type, typename... Args>
inline void write(char midChar, Type t, Args... args) {
write(midChar, t), write(midChar, args...);
}
} // namespace Fast_IO using Fast_IO::read;
using Fast_IO::write; const int SIZE = 5e4 + 5;
namespace ChainForwardStar {
const int SIZE = ::SIZE;
int tot_, Vert[SIZE];
int Head[SIZE], Next[SIZE]; void AddLine(int x, int y) {
Vert[++tot_] = y;
Next[tot_] = Head[x];
Head[x] = tot_;
}
} // namespace ChainForwardStar using CFS::Head;
using CFS::Vert;
using CFS::Next;
using CFS::AddLine; namespace TreeChainSplitting {
const int SIZE = ::SIZE;
const int MOD = 201314;
int n, m, tot, d[SIZE];
int fa[SIZE], size[SIZE];
int son[SIZE], top[SIZE];
int dfn[SIZE], rnk[SIZE];
int ask[SIZE], ans[SIZE];
vector < int > GFY[SIZE];
vector < int > FI[SIZE];
int sum[SIZE << 2], lf[SIZE << 2]; void Prepare(int x) {
size[x] = 1, d[x] = d[fa[x]] + 1;
for (ForGraph)
Prepare(y), size[x] += size[y], CheckSize;
} void Prepare(int x, int tp) {
rnk[dfn[x] = ++tot] = x, top[x] = tp;
if (son[x]) Prepare(son[x], tp);
for (ForGraph) if (y ^ son[x]) Prepare(y, y);
} #define ls (k << 1)
#define rs (k << 1 | 1)
#define L_RECUR ls, l, mid, x, y
#define R_RECUR rs, mid + 1, r, x, y
#define UpdateSons(k, l, r) if (lf[k]) sum[ls] = (sum[ls] + 1LL * lf[k] * (mid - l + 1) % MOD) % MOD, \
sum[rs] = (sum[rs] + 1LL * lf[k] * (r - mid) % MOD) % MOD, lf[ls] += lf[k], \
lf[rs] += lf[k], lf[k] = 0
#define UpdateMessages(k) sum[k] = (sum[ls] + sum[rs]) % MOD
void ModifyChain(int k, int l, int r, int x, int y) {
if (l >= x && r <= y) sum[k] = (sum[k] + r - l + 1) % MOD, ++lf[k];
else {
UpdateSons(k, l, r);
if (mid >= x) ModifyChain(L_RECUR);
if (mid < y) ModifyChain(R_RECUR);
UpdateMessages(k);
}
} void ModifySubTree(int x) {
if (x) ModifyChain(1, 1, n, dfn[top[x]], dfn[x]), ModifySubTree(x = fa[top[x]]);
} int QueryChain(int k, int l, int r, int x, int y, int res = 0) {
if (l >= x && r <= y) return sum[k];
UpdateSons(k, l, r);
if (mid >= x) res += QueryChain(L_RECUR, 0);
if (mid < y) res += QueryChain(R_RECUR, 0);
return res;
} int QuerySubTree(int x, int res = 0) { // luogu-only
if (x) QuerySubTree((res += QueryChain(1, 1, n, dfn[top[x]], dfn[x]), x = fa[top[x]]), res); else return res;
} // int QuerySubTree(int x, int res = 0) { // loj-only
// while (x) {
// res += QueryChain(1, 1, n, dfn[top[x]], dfn[x]);
// x = fa[top[x]];
// }
// return res;
// }
} // namespace TreeChainSplitting namespace ProblemSolver {
void main() {
read(TCS::n), read(TCS::m);
for (int i = 2; i <= TCS::n; ++i) read(TCS::fa[i]), AddLine(++TCS::fa[i], i);
TCS::Prepare(1), TCS::tot = 0, TCS::Prepare(1, 1);
int L, R;
for (int i = 1; i <= TCS::m; ++i) read(L, R, TCS::ask[i]), TCS::FI[L].push_back(i), TCS::GFY[R + 1].push_back(i), ++TCS::ask[i];
for (int i = 1; i <= TCS::n; ++i) {
TCS::ModifySubTree(i);
for (auto it : TCS::GFY[i]) TCS::ans[it] += TCS::QuerySubTree(TCS::ask[it]);
for (auto it : TCS::FI[i]) TCS::ans[it] -= TCS::QuerySubTree(TCS::ask[it]);
}
for (int i = 1; i <= TCS::m; ++i) write(io_l, (TCS::ans[i] % TCS::MOD + TCS::MOD) % TCS::MOD);
}
} // namespace ProblemSover signed main() {
PS::main();
}

25.「2018 集训队互测 Day 3」北校门外的未来

前言

这道题绝对是我目前来说做题用时最长的一道题(总共花了我五天时间左右)

说一下我这几天的心路历程吧:

Day1:嗯?我好像发现了一道有趣的题目?洛谷上好像没有。。算了,做一下试试。。。

Day2:cow,这道题是道什么题?口胡一下LCT?算了算了看看题解。。。笛卡尔树是什么??学吧学吧。。。

Day3:上半天:cow学不动了。。。更一下博客吧(广告位);下半天:继续吧。。。

Day4:上半天:好像有点想法了。。。完善一下。。找找锅。。;下半天(确切的说是晚上。。):开始码咯!

Day5:上半天:调了5个小时后过了;下半天:写写题解咯

正文

Description

如果你不想阅读故事,请直接跳到题意部分。

转眼间,已是三年流转。

夏日法桐的绿荫,代替了秋季的萧索,衬托着 LCR 和神犇成长的背影。

身后的北校门,也不再是当年学生试图摧毁的,束缚自由的枷锁,而成了青春记忆的符号。

又到了神犇和 LCR 相遇的地方 —— 北校门外的树下。这棵神奇的树早已不是 K 项树的形态。每时每刻,它都以新的独特方式演绎着生命。

谁也没有开口,他和她静静地注视着魔法般生长的自然种子。初始时,这棵树只有一个点,LCR 将其标号为 \(1\)。此后,每过一段时间,就会有一个新节点从原有的某个点出生长出来,LCR 会给它分配一个尚未使用过的不超过 \(n\) 的正整数编号。

树中生活着一些小精灵。它们总停留在节点上,如果一个精灵在编号 \(u\) 的节点,那么它可以一步跳到任何编号 \(v\) 的满足 \(u,v\) 之间的简单路径上不存在异于 \(u,v\) 的编号大于 \(\min(u,v)\) 的点处。

在观察这棵树的过程中,LCR 产生了一些疑问。她想知道,对于一对节点编号 \(u,v\),从节点 \(u\) 跳到节点 \(v\) 最少需要几步。

神犇轻松地解决了这些问题。最终,树渐渐停止了生长,但神犇仍然陶醉其中。

一只飘渺的手搭上了神犇的肩膀。他回过头,看到 LCR 正在微笑。

“亲爱的少年,神犇君。”

“你是否想过,为什么精灵会依照我编号的法则而运动呢?”

神犇一时语塞。瞬间,LCR 的手变得虚幻了起来,如同明灭的火炬。

“你的成长,是这变化世界的一个切面。感谢你与我度过的时光。不要留恋 …… 我的随风飘散,正是与你们同在。”

“再见了,神犇君。”

LCR 消失了,神犇机械地转过身,却发现背后的树也已消失无踪。

“神犇,神犇 ……” 茫然若失的神犇背后传来了渐行渐近的呼叫。神犇转过身,发现机房里的蒟蒻 LCA 正向他跑来。

“又是一年毕业季了呢。学长你还好吗?”

“也许吧。” 神犇望向校门外的树原先的位置,“LCR 走了,但她的背影会吸引着我们的人生。”

LCA 沉默了。他和神犇一同望向树消失的地方,持续片刻。

“所谓中二的幻想,才是我们相对的有限的主观能动性唯一的立场吧,不要给自己设限啊,LCA。我们去追寻她 …… 追寻自然的精灵。也许这就是我们的初心也说不定。”

这次是 LCA 目送神犇的背影渐行渐远了。

“再见了,学长。”

某少女附中,又迎来了新的一年。

那么,你能够回答 LCR 提出的问题吗?


题意

对于一棵树 \(T=(V,E)\),\(V\) 中每个点有一个互不相同的正整数标号。我们用点 \(i\) 表示编号为 \(i\) 的点。

定义这棵树的谷图为 \(G(T)=(V,E')\)。\(G(T)\) 是无向简单图。存在边 \((u,v)\in E'\) 当且仅当在 \(T\) 中,不存在一个异于 \(u,v\) 的点 \(x\) 满足 \(x\) 在从 \(u\) 到 \(v\) 的简单路径上且其编号大于 \(\min(u,v)\)。

有一棵树 \(T\),初始时只有一个点,编号为 \(1\),接下来有 \(q\) 次操作,操作有以下两种:

  • \(\texttt{1 u v}\) 表示加入一个编号为 \(v\) 的节点并与当前编号为 \(u\) 的节点相连(保证任何时刻不会有两个编号相同的节点);
  • \(\texttt{2 u v}\) 表示查询 \(G(T)\) 中点 \(u\) 到 \(v\) 的最短路(每条边长度均为 \(1\))。

请你回答所有查询。


题目好长咳咳咳。。。(最关键的是我读完过后重读一遍发现背景与题目无关。。。靠!

不扯了说正事儿。。。

动态问题其实很烦人对吧?这里我们可以假设原问题为静态问题。

静态问题就很好处理对吧?都是显然做法。那么问题又回到动态问题,对于加点操作,事实上我的做法十分**,利用Splay的性质乱搞。打出了一个像模拟一样的东西

其实我也不太清楚我是怎么过的。。。因为我的做法很玄学+暴力,所以。。。大家意会一下。

如果实在要看题解的话,我这里有一篇,不过估计也没人能看懂,反正你看了就知道了。至于其他的题解想都别想了我FQ去隔壁Google都没找到,基本上是找不

道的。。。Link

放个代码,大家意会一下。。。(404行好评)

#pragma GCC diagnostic error "-std=c++11"
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define SIZE_CHECKER ((size[son[x]] < size[y]) && (son[x] = y))
#define pii pair < int , int >
#define pll pair < LL, LL >
#define mid ((l + r) >> 1)
#define mp make_pair
#define fir first
#define sec second
#define pub push_back
#define pob pop_back using namespace std;
typedef long long LL; #define io_e '\0'
#define io_s ' '
#define io_l '\n'
// #define _DEBUG_ 1 // debug toggle
namespace Fast_IO {
#ifndef _DEBUG_
#define gc() (iS == iT ? (iT = (iS = ibuff) + fread(ibuff, 1, SIZ, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
#else
#define gc() getchar()
#endif
const int SIZ = 1 << 21 | 1;
char *iS, *iT, ibuff[SIZ], obuff[SIZ], *oS = obuff, *oT = oS + SIZ - 1, fu[110], c;
int fr;
inline void ioout() {
fwrite(obuff, 1, oS - obuff, stdout);
oS = obuff;
}
template <class Type>
inline void read(Type& x) {
x = 0;
Type y = 1;
for (c = gc(); (c > '9' || c < '0') && c ^ '-'; c = gc())
;
c == '-' ? y = -1 : x = (c & 15);
for (c = gc(); c >= '0' && c <= '9'; c = gc()) x = x * 10 + (c & 15);
x *= y;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
inline void read(char* s) {
register char ch = gc();
for (; blank(ch); ch = gc())
;
for (; !blank(ch); ch = gc()) *s++ = ch;
*s = 0;
}
inline void read(char& c) {
for (c = gc(); blank(c); c = gc())
;
}
template <typename Type, typename... Args>
inline void read(Type& t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char* t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char& t, Args&... args) {
read(t), read(args...);
}
template <class Type>
inline void write(char lastChar, Type x) {
if (x < 0)
*oS++ = '-', x = -x;
if (x == 0)
*oS++ = '0';
while (x) fu[++fr] = x % 10 + '0', x /= 10;
while (fr) *oS++ = fu[fr--];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x[]) {
for (register int i = 0; x[i]; ++i) *oS++ = x[i];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x) {
*oS++ = x;
*oS++ = lastChar;
ioout();
}
template <typename Type, typename... Args>
inline void write(char midChar, Type t, Args... args) {
write(midChar, t), write(midChar, args...);
}
} // namespace Fast_IO using Fast_IO::read;
using Fast_IO::write; const int SIZE = 5e5 + 5;
vector < vector < int > > G(SIZE);
int n, m, st[SIZE], tp, F[SIZE];
int _time[SIZE], _down[SIZE];
int OP[SIZE], U[SIZE], V[SIZE]; void init(int n) {
for (int i = 1; i <= n; ++i)
F[i] = i;
} int find(int x) {
if (x ^ F[x]) F[x] = find(F[x]);
return F[x];
} namespace CartesianTree {
vector < vector < int > > GCT(SIZE);
int son[SIZE], fa[SIZE];
int top[SIZE], size[SIZE];
int low[SIZE], d[SIZE];
int tot, ls[SIZE], rs[SIZE]; void Prepare1(int x) {
while (_time[st[tp]] > _time[x] && tp) _down[st[tp--]] = x;
st[++tp] = x;
size[x] = 1;
d[x] = d[fa[x]] + 1;
for (auto y : GCT[x])
Prepare1((fa[y] = x, y)), size[x] += size[y], SIZE_CHECKER;
} void Prepare2(int x) {
ls[x] = ++tot;
if (!top[x]) top[x] = x;
if (!son[x]) return (void)(rs[x] = tot);
top[son[x]] = top[x];
for (auto y : GCT[x]) Prepare2(y);
rs[x] = tot;
} int GetLCA(int x, int y) {
while (top[x] ^ top[y])
d[top[x]] > d[top[y]] ? x = fa[top[x]] : y = fa[top[y]];
return d[x] < d[y] ? x : y;
} int Behavior(int x, int y) {
while (top[x] ^ top[y]) if (fa[x = top[x]] ^ y) x = fa[x]; else return x;
return son[y];
} bool FindRule(int x, int y) {
return ls[x] < ls[y];
} bool CheckForFun(int x, int y) {
int std = *(upper_bound(GCT[y].begin(), GCT[y].end(), x, FindRule) - 1);
return !(ls[low[std]] > rs[x] || ls[x] > ls[low[std]]);
} void Main() {
init(n);
for (int i = 1; i <= n; ++i)
for (auto j : G[i])
if (i > find(j)) GCT[i].pub(find(j)), F[find(j)] = i;
Prepare1(n), Prepare2(n);
for (int i = 1; i <= n; ++i)
for (auto j : G[i])
if (i > j) low[Behavior(j, i)] = j;
}
} // namespace CartesianTree
#define CT CartesianTree namespace LinkCutTree {
struct SPLAY {
int ch[2];
int fa;
int sum;
int key;
} data[SIZE];
int next[SIZE], root[SIZE];
#define WhichSon(x) (data[data[x].fa].ch[1] == x) void UpdateMessages(int x) {
data[x].sum = data[data[x].ch[0]].sum + data[data[x].ch[1]].sum + data[x].key;
} void RotateNode(int x) {
int y = data[x].fa, z = data[y].fa;
int k = WhichSon(x);
if (root[y]) root[y] = 0, root[x] = root[y] ^ 1;
else data[z].ch[data[z].ch[1] == y] = x;
data[x].fa = data[y].fa;
data[y].ch[k] = data[x].ch[k ^ 1];
if (data[y].ch[k]) data[data[x].ch[k ^ 1]].fa = y;
data[x].ch[k ^ 1] = y;
data[y].fa = x;
UpdateMessages(y), UpdateMessages(x);
} void SplayToRoot(int x) {
for (int y; !root[x]; RotateNode(x))
if (!root[y = data[x].fa])
RotateNode(data[data[y].fa].ch[0] ^ y ^ data[y].ch[0] ^ x ? x : y);
} void AccessEdge(int x) {
for (int y = 0; x; x = data[y = x].fa) {
SplayToRoot(x);
if (data[x].ch[1]) root[data[x].ch[1]] = 1;
if (data[x].ch[1] = y) root[y] = 0;
UpdateMessages(x);
}
} void Behavior(int u, int v) {
if (u > v) {
F[v] = u;
data[v].fa = u;
data[v].key = 1;
data[v].sum = 1;
return ;
}
int x = _down[v], rhs = u;
SplayToRoot(x);
int rsp = 0, cpy = 0, now = data[x].ch[0];
if (now) {
while (data[now].ch[1]) now = data[now].ch[1];
SplayToRoot(now);
while (data[x].fa ^ now) RotateNode(x);
root[x] = 1;
data[now].ch[1] = 0;
UpdateMessages(now);
}
now = data[x].fa;
if (next[now] == x) next[now] = v;
data[v].key = data[v].sum = data[x].key;
if (now) next[v] = x;
SplayToRoot(x);
data[x].fa = v;
data[v].fa = now;
data[x].key = 0;
UpdateMessages(x); while (u) {
SplayToRoot(u);
if (data[u].ch[1]) root[data[u].ch[1]] = 1;
data[u].ch[1] = rsp;
if (data[u].ch[1]) root[rsp] = 0;
UpdateMessages(u);
if (data[u].sum) {
x = u;
while (233)
if (data[x].ch[1] && data[data[x].ch[1]].sum) x = data[x].ch[1];
else if (!data[x].key) x = data[x].ch[0];
else break;
SplayToRoot(x);
if (x >= v) break;
u = data[x].ch[0];
if (u) {
while (data[u].ch[1]) u = data[u].ch[1];
SplayToRoot(u);
data[u].ch[1] = 0;
root[x] = 1;
UpdateMessages(u);
}
else u = data[x].fa;
if (u >= v) break;
SplayToRoot(rhs);
if (now = data[rhs].ch[1]) root[now] = 1, data[rhs].ch[1] = 0;
if (now = next[rhs]) {
SplayToRoot(now);
data[now].fa = u;
data[now].key = 1;
UpdateMessages(now);
next[rhs] = 0;
}
u = F[x];
rhs = F[x];
data[x].key = 0;
UpdateMessages(x);
SplayToRoot(x);
while (data[x].ch[1]) x = data[x].ch[1];
SplayToRoot(x);
data[x].ch[1] = cpy;
if (data[x].ch[1]) {
data[cpy].fa = x, root[cpy] = 0, data[x].ch[1] = cpy;
while (data[cpy].ch[0]) cpy = data[cpy].ch[0];
next[x] = cpy;
SplayToRoot(cpy);
}
SplayToRoot(x);
cpy = x;
rsp = 0;
}
else rsp = u, u = data[u].fa;
}
F[v] = F[_down[v]];
F[_down[v]] = v;
if (!F[v]) {
SplayToRoot(v);
x = v;
while (data[x].ch[1]) x = data[x].ch[1];
if (cpy) {
data[x].ch[1] = cpy;
root[cpy] = 0;
data[cpy].fa = x;
while (data[cpy].ch[0]) cpy = data[cpy].ch[0];
next[x] = cpy;
SplayToRoot(cpy);
}
SplayToRoot(v);
x = data[v].ch[1];
while (data[x].ch[0]) x = data[x].ch[0];
data[x].key = 1;
UpdateMessages(x);
SplayToRoot(x);
}
else if (cpy) {
x = cpy;
while (data[x].ch[0]) x = data[x].ch[0];
data[x].key = 1;
UpdateMessages(x);
SplayToRoot(x);
data[x].fa = v;
}
} pii GetDis(int x, int t) {
if (x ^ t) {
AccessEdge(x);
SplayToRoot(x);
int st = x;
int fir = 0, sec = 0;
while (x)
if (x < t) sec = x, x = data[x].ch[0];
else x = data[x].ch[1];
SplayToRoot(sec);
x = data[sec].ch[1];
fir = data[x].sum;
if (!fir) return mp(0, st);
while (233) {
if (data[data[x].ch[0]].sum) x = data[x].ch[0];
else if (!data[x].key) sec = x, x = data[x].ch[1];
else {
if (data[x].ch[0]) {
x = data[x].ch[0];
while (data[x].ch[1]) x = data[x].ch[1];
sec = x;
}
break;
}
}
return mp(fir, sec);
}
else return mp(0, x);
} int GetAnswers(int x, int y) {
if (x ^ y) {
if (x > y) x ^= y ^= x ^= y;
int sys = CT::GetLCA(x, y);
if (sys ^ y) {
pii t1 = GetDis(x, sys);
pii t2 = GetDis(y, sys);
return t1.fir + t2.fir + ((CT::CheckForFun(t1.sec, sys)
&& CT::CheckForFun(t2.sec, sys)) ^ 1) + 2;
}
else {
pii t = GetDis(x, sys);
return t.fir + (CT::CheckForFun(t.sec, sys) ^ 1) + 1;
}
}
else return 0;
}
} // namespace LinkCutTree
#define LCT LinkCutTree namespace SOLVER {
void Main() {
read(m), read(m);
n = 1;
for (int i = 1; i <= m; ++i) {
read(OP[i], U[i], V[i]);
if (OP[i] ^ 1) continue;
G[U[i]].pub(V[i]);
G[V[i]].pub(U[i]);
_time[V[i]] = i;
n = max(n, V[i]);
}
for (int i = 1; i <= n; ++i) LCT::root[i] = true;
CT::Main();
F[1] = 0;
for (int i = 1; i <= m; ++i) {
if (OP[i] ^ 2) LCT::Behavior(U[i], V[i]);
else write(io_l, LCT::GetAnswers(U[i], V[i]));
}
}
} signed main() {
SOLVER::Main();
}

26.P1121 环状最大两段子段和

给出一段环状序列,即认为\(A_1\)和\(A_N\)是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。


一共三个关键词:

环状

最大

两段子段和

显然这是一道DP题。。。然而我们可以把它搞成线段树!

普通的最大子段和相信大家都能够用线段树来完成。

无非就是记录区间和、区间最大子段和、区间最大前缀和、区间最大后缀和

然后合并时更新信息即可

那么两段子段和该怎么搞呢?

其实也不费脑子废笔

首先套路的讨论这两段的位置,我们可以发现有5种情况(画画图示意一下可能很丑反正意思你们能懂就行

总结出来我们需要在线段树里维护的东西有:

  1. 区间和
  2. 区间最大前缀
  3. 区间最大后缀
  4. 区间最大子段和
  5. 区间最大前缀+后缀
  6. 区间最大中间+后缀
  7. 区间最大前缀+中间
  8. 区间两段最大子段和

每行长度不下降看着还是很爽的。。。

具体实现看代码中的注释

// 省略快读和一堆预处理命令

const int SIZE = 4e5 + 5;
const int INF = ~0U >> 1;
struct TreeNode { // 这里的意思应该很明确
int sum;
int maxSum;
int maxSumDouble;
int maxPrefixSum;
int maxSuffixSum;
int maxPrePlusSuf;
int maxPrePlusMid;
int maxSufPlusMid;
} data[SIZE<<2];
int ints[SIZE<<1], ans = -INF, n; TreeNode UpdateMessages(TreeNode x, TreeNode y) { // 这是重点
TreeNode res;
res.sum = x.sum + y.sum; // 区间和标记上传
res.maxSum = max(x.maxSum, y.maxSum); // // 区间最大子段和标记上传
res.maxSum = max(res.maxSum, x.maxSuffixSum + y.maxPrefixSum); // 再把自己和两个儿子的最大前/后缀和的和比较
res.maxPrefixSum = max(x.maxPrefixSum, x.sum + y.maxPrefixSum); // 其实下面都差不多
res.maxSuffixSum = max(y.maxSuffixSum, y.sum + x.maxSuffixSum); // 实在不懂看上面的图
res.maxPrePlusSuf = max(x.maxPrefixSum + y.maxSuffixSum, x.sum + y.maxPrePlusSuf); // 看了就明白了。。。
res.maxPrePlusSuf = max(res.maxPrePlusSuf, y.sum + x.maxPrePlusSuf);
res.maxPrePlusMid = max(x.maxPrePlusMid, x.sum + y.maxPrePlusMid);
res.maxPrePlusMid = max(res.maxPrePlusMid, x.maxPrefixSum + y.maxSum);
res.maxPrePlusMid = max(res.maxPrePlusMid, x.maxPrePlusSuf + y.maxPrefixSum);
res.maxSufPlusMid = max(y.maxSufPlusMid, y.sum + x.maxSufPlusMid);
res.maxSufPlusMid = max(res.maxSufPlusMid, y.sum + x.maxSum);
res.maxSufPlusMid = max(res.maxSufPlusMid, y.maxPrePlusSuf + x.maxSuffixSum);
res.maxSumDouble = max(x.maxSumDouble, y.maxSumDouble);
res.maxSumDouble = max(res.maxSumDouble, x.maxSum + y.maxSum);
res.maxSumDouble = max(res.maxSumDouble, x.maxSufPlusMid + y.maxPrefixSum);
res.maxSumDouble = max(res.maxSumDouble, x.maxSuffixSum + y.maxPrePlusMid);
return res;
} void Initialization(int k, int l) { // 给线段树结点赋初值
data[k].sum = data[k].maxPrefixSum = data[k].maxSuffixSum = data[k].maxSum = ints[l];
data[k].maxSumDouble = data[k].maxPrePlusSuf = data[k].maxPrePlusMid = data[k].maxSufPlusMid = -INF;
} void BuildTree(int k, int l, int r) {
if (l ^ r) BuildTree(ls, l, mid), BuildTree(rs, mid + 1, r), data[k] = UpdateMessages(data[ls], data[rs]);
else Initialization(k, l);
} TreeNode GetAnswers(int k, int l, int r, int x, int y) {
if (l ^ x || r ^ y) {
if (mid >= y) return GetAnswers(ls, l, mid, x, y);
if (mid < x) return GetAnswers(rs, mid + 1, r, x, y);
return UpdateMessages(GetAnswers(ls, l, mid, x, mid), GetAnswers(rs, mid + 1, r, mid + 1, y));
} else return data[k];
} signed main() {
read(n);
for (int i = 1; i <= n; ++i) read(ints[i]), ints[i + n] = ints[i];
BuildTree(1, 1, n << 1);
for (int i = 1; i <= n; ++i) ans = max(ans, GetAnswers(1, 1, n << 1, i, i + n - 1).maxSumDouble);
write(io_l, ans);
}

27.【中午的题目】分糖果

小 Z 带着新买的糖果来拜访舅舅家,舅舅家的 \(K\) 个孩子看见小 Z 带着糖果来拜访变得欣喜若狂,他们都希望吃到好吃的糖果。正当小 Z 准备给 \(K\) 个孩子分糖果时,舅舅却让小 Z 尽量少分点,免得孩子们蛀牙。

小 Z 带来的糖果比较特别,一共有 \(N\) 个糖果连成一串,编号为 \(1\ldots N\),第 \(i\) 个糖果有一个数值 \(a[i]\) 表示蛀牙的可能性,数值越大的糖果越容易导致蛀牙,多个糖果的蛀牙值认为是各个糖果的蛀牙值之和。

现在小 Z 打算取 \(N\) 个糖果的前若干个,分成 \(K\) 段分给 \(K\) 个孩子。小 Z 好奇,他该怎么分糖果,才能使得分到糖果蛀牙值最大的孩子尽可能不蛀牙


首先暴力做法显然。

首先前缀和然后对前缀和离散化

二分答案然后check里面dp,\(dp_i\) 为区间 \([1,i]\) 最多能分成的块数。

dp方程显然为 \(dp_i=max{dp_j}+1\)。

这样转移是 \(\Theta(n^2)\) 的,显然T飞。

考虑树状数组优化,需要区间最大值和单点修改操作。

时间复杂度 \(\Theta(n\log^2n)\)

#pragma GCC diagnostic error "-std=c++11"
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define ls (k << 1)
#define rs (k << 1 | 1)
#define SIZE_CHECKER(x, y) ((size[son[x]] < size[y]) && (son[x] = y))
#define PII pair < __int64 , __int64 >
#define PLL pair < LL, LL >
#define mid ((l + r) >> 1)
#define mp make_pair
#define fir first
#define sec second
#define pb push_back
#define R register using namespace std;
#ifndef __int8
typedef char __int8;
#endif
#ifndef __uint8
typedef unsigned char __uint8;
#endif
#ifndef __int16
typedef short __int16;
#endif
#ifndef __uint16
typedef unsigned short __uint16;
#endif
#ifndef __int32
typedef int __int32;
#endif
#ifndef __int64
typedef long long __int64;
#endif
#ifndef __uint32
typedef unsigned int __uint32;
#endif
#ifndef __uint64
typedef unsigned long long __uint64;
#endif #define io_e '\0'
#define io_s ' '
#define io_l '\n'
#define _DEBUG_ 1 // debug toggle
namespace Fast_IO {
#ifndef _DEBUG_
#define gc() (iS == iT ? (iT = (iS = ibuff) + fread(ibuff, 1, SIZ, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
#else
#define gc() getchar()
#endif
const __int64 SIZ = 1 << 21 | 1;
char *iS, *iT, ibuff[SIZ], obuff[SIZ], *oS = obuff, *oT = oS + SIZ - 1, fu[110], c;
__int64 fr;
inline void ioout() {
fwrite(obuff, 1, oS - obuff, stdout);
oS = obuff;
}
template <class Type>
inline void read(Type& x) {
x = 0;
Type y = 1;
for (c = gc(); (c > '9' || c < '0') && c ^ '-'; c = gc())
;
c == '-' ? y = -1 : x = (c & 15);
for (c = gc(); c >= '0' && c <= '9'; c = gc()) x = x * 10 + (c & 15);
x *= y;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
inline void read(char* s) {
register char ch = gc();
for (; blank(ch); ch = gc())
;
for (; !blank(ch); ch = gc()) *s++ = ch;
*s = 0;
}
inline void read(char& c) {
for (c = gc(); blank(c); c = gc())
;
}
template <typename Type, typename... Args>
inline void read(Type& t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char* t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char& t, Args&... args) {
read(t), read(args...);
}
template <class Type>
inline void write(char lastChar, Type x) {
if (x < 0)
*oS++ = '-', x = -x;
if (x == 0)
*oS++ = '0';
while (x) fu[++fr] = x % 10 + '0', x /= 10;
while (fr) *oS++ = fu[fr--];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x[]) {
for (register __int64 i = 0; x[i]; ++i) *oS++ = x[i];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x) {
*oS++ = x;
*oS++ = lastChar;
ioout();
}
template <typename Type, typename... Args>
inline void write(char midChar, Type t, Args... args) {
write(midChar, t), write(midChar, args...);
}
} // namespace Fast_IO using Fast_IO::read;
using Fast_IO::write; const __int64 SIZE = 1e6 + 5;
const __int64 INF = 0x7fffffff;
__int64 a[SIZE], bit[SIZE];
__int64 dp[SIZE], n, k, T;
vector < __int64 > disc; void add(__int64 x, __int64 y) { for (; x > 0; x -= x & -x) bit[x] = max(bit[x], y); }
__int64 ask(__int64 x, __int64 lim, __int64 res = -INF) { for (; x < lim; x += x & -x) res = max(res, bit[x]); return res; } void Discretization(__int64 x) {
disc.clear();
disc.pb(0);
for (__int64 i = 1; i <= n; ++i) disc.pb(a[i]);
sort(disc.begin(), disc.end());
disc.erase(unique(disc.begin(), disc.end()), disc.end());
} bool Check(__int64 x) {
Discretization(x);
for (__int64 i = 1; i <= (__int64)disc.size(); ++i) {
for (__int64 j = i; j < (__int64)disc.size(); j += j & -j) bit[i] = -INF;
for (__int64 j = i; j > 0; j -= j & -j) bit[i] = -INF;
}
*dp = 0;
add(lower_bound(disc.begin(), disc.end(), 0) - disc.begin() + 1, *dp);
for (__int64 i = 1; i <= n; ++i) {
dp[i] = ask(lower_bound(disc.begin(), disc.end(), a[i] - x) - disc.begin() + 1, disc.size() + 1) + 1;
add(lower_bound(disc.begin(), disc.end(), a[i]) - disc.begin() + 1, dp[i]);
if (k <= dp[i]) return 1;
}
return 0;
} signed main() {
for (read(T); T; --T) {
read(n, k);
for (__int64 i = 1; i <= n; ++i) read(a[i]), a[i] += a[i - 1];
__int64 l = -INF * SIZE, r = SIZE * INF;
while (l < r - 1)
if (Check(mid)) r = mid;
else l = mid;
write(io_l, r);
}
return 0;
}

28.P2163 [SHOI2007]园丁的烦恼

很久很久以前,在遥远的大陆上有一个美丽的国家。统治着这个美丽国家的国王是一个园艺爱好者,在他的皇家花园里种植着各种奇花异草。

有一天国王漫步在花园里,若有所思,他问一个园丁道: “最近我在思索一个问题,如果我们把花坛摆成六个六角形,那么……”

“那么本质上它是一个深度优先搜索,陛下”,园丁深深地向国王鞠了一躬。

“嗯……我听说有一种怪物叫九头蛇,它非常贪吃苹果树……”

“是的,显然这是一道经典的动态规划题,早在N元4002年我们就已经发现了其中的奥秘了,陛下”。

“该死的,你究竟是什么来头?”

“陛下息怒,干我们的这行经常莫名其妙地被问到和OI有关的题目,我也是为了预防万一啊!” 王者的尊严受到了伤害,这是不可容忍的。

看来一般的难题是难不倒这位园丁的,国王最后打算用车轮战来消耗他的实力: “年轻人,在我的花园里的每一棵树可以用一个整数坐标来表示,一会儿,我的骑士们会来轮番询问你某一个矩阵内有多少树,如果你不能立即答对,你就准备走人吧!”说完,国王气呼呼地先走了。

这下轮到园丁傻眼了,他没有准备过这样的问题。所幸的是,作为“全国园丁保护联盟”的会长——你,可以成为他的最后一根救命稻草。


。。。居然没有看到主席树题解。

这差不多就是一道主席树的板题了。

首先对x排序,然后动态开点一个一个的insert进去。

对于询问直接就二分取出横轴的范围l和r

然后询问root[r]-root[l-1]就好了

#pragma GCC diagnostic error "-std=c++11"
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define ls (data[rt].l)
#define rs (data[rt].r)
#define SIZE_CHECKER(x, y) ((size[son[x]] < size[y]) && (son[x] = y))
#define PII pair < int , int >
#define PLL pair < LL, LL >
#define mid ((l + r) >> 1)
#define mp make_pair
#define fir first
#define sec second
#define pb push_back
#define R register using namespace std;
#ifndef __int8
typedef char __int8;
#endif
#ifndef __uint8
typedef unsigned char __uint8;
#endif
#ifndef __int16
typedef short __int16;
#endif
#ifndef __uint16
typedef unsigned short __uint16;
#endif
#ifndef __int32
typedef int __int32;
#endif
#ifndef __int64
typedef long long __int64;
#endif
#ifndef __uint32
typedef unsigned int __uint32;
#endif
#ifndef __uint64
typedef unsigned long long __uint64;
#endif #define io_e '\0'
#define io_s ' '
#define io_l '\n'
#define _DEBUG_ 1 // debug toggle
namespace Fast_IO {
#ifndef _DEBUG_
#define gc() (iS == iT ? (iT = (iS = ibuff) + fread(ibuff, 1, SIZ, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
#else
#define gc() getchar()
#endif
const int SIZ = 1 << 21 | 1;
char *iS, *iT, ibuff[SIZ], obuff[SIZ], *oS = obuff, *oT = oS + SIZ - 1, fu[110], c;
int fr;
inline void ioout() {
fwrite(obuff, 1, oS - obuff, stdout);
oS = obuff;
}
template <class Type>
inline void read(Type& x) {
x = 0;
Type y = 1;
for (c = gc(); (c > '9' || c < '0') && c ^ '-'; c = gc())
;
c == '-' ? y = -1 : x = (c & 15);
for (c = gc(); c >= '0' && c <= '9'; c = gc()) x = x * 10 + (c & 15);
x *= y;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
inline void read(char* s) {
register char ch = gc();
for (; blank(ch); ch = gc())
;
for (; !blank(ch); ch = gc()) *s++ = ch;
*s = 0;
}
inline void read(char& c) {
for (c = gc(); blank(c); c = gc())
;
}
template <typename Type, typename... Args>
inline void read(Type& t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char* t, Args&... args) {
read(t), read(args...);
}
template <typename... Args>
inline void read(char& t, Args&... args) {
read(t), read(args...);
}
template <class Type>
inline void write(char lastChar, Type x) {
if (x < 0)
*oS++ = '-', x = -x;
if (x == 0)
*oS++ = '0';
while (x) fu[++fr] = x % 10 + '0', x /= 10;
while (fr) *oS++ = fu[fr--];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x[]) {
for (register int i = 0; x[i]; ++i) *oS++ = x[i];
*oS++ = lastChar;
ioout();
}
inline void write(char lastChar, char x) {
*oS++ = x;
*oS++ = lastChar;
ioout();
}
template <typename Type, typename... Args>
inline void write(char midChar, Type t, Args... args) {
write(midChar, t), write(midChar, args...);
}
} // namespace Fast_IO using Fast_IO::read;
using Fast_IO::write; const int SIZE = 5e5 + 5;
const int INF = 1e7;
int n, m, tot, rt[SIZE];
struct TreeNode {
int l, r;
int sum;
} data[SIZE << 5];
struct TwoNode {
int x, y;
} ints[SIZE];
struct BinarySeach {
int upper_bound(int x) {
int l = 1, r = n + 1;
while (l < r - 1) if (ints[mid].x > x) r = mid; else l = mid;
return l;
} int lower_bound(int x) {
int l = 0, r = n;
while (l < r - 1) if (ints[mid].x >= x) r = mid; else l = mid;
return r;
}
} BS; bool cmp(const TwoNode& x, const TwoNode& y) {
return x.x < y.x;
} void update(int &rt, int l, int r, int x) {
data[++tot] = data[rt];
++data[rt = tot].sum;
if (l ^ r) if (mid >= x) update(ls, l, mid, x); else update(rs, mid + 1, r, x); else return ;
} int queryf(int rt, int l, int r, int x, int y) {
if (l > y || r < x || !rt) return 0;
else if (l >= x && r <= y) return data[rt].sum;
else return queryf(ls, l, mid, x, y) + queryf(rs, mid + 1, r, x, y);
} signed main() {
read(n, m);
for (int i = 1; i <= n; ++i) read(ints[i].x, ints[i].y);
sort(ints + 1, ints + 1 + n, cmp);
for (int i = 1; i <= n; ++i) update(rt[i] = rt[i - 1], 0, INF, ints[i].y);
for (int i = 1, a, b, c, d; i <= m; ++i) {
read(a, b, c, d);
write(io_l, queryf(rt[BS.upper_bound(c)], 0, INF, b, d) - queryf(rt[BS.lower_bound(a) - 1], 0, INF, b, d));
}
return 0;
}

29.P5220 特工的信息流

\(\text{TYM}\) 所在的国家有 \(n\) 个城市,编号为 \(1,\dots,n\),由 \(n - 1\) 条双向道路连接。保证任意两个城市间都有唯一的简单路径。

以及,每个城市都有一个信息流的流量 \(a_i\)。

\(\text{TYM}\) 一共要执行 \(m_0\) 个任务,每个任务给定两个城市 \(s,t\),其执行过程如下:

第一个时刻,他从城市 \(s\) 出发,以每个时刻移动到下一个城市的速度,走 \(s,t\) 之间的简单路径到 \(t\)。

每到达一个城市,他都会把这个城市的信息流 \(a_i\) 发送到经过的每个城市。

我们约定,他到达一个城市的同一时刻也会把这个城市的信息流发送给这个城市。我们定义一个城市的价值为这个城市所接受到的信息流的乘积。

请你求出每个任务中,\(s\) 到 \(t\) 的简单路径上经过的城市的价值的总和对 \(20924\) 取模的结果。

此外,不幸地,由于侵略者同时也在行动,所以在他执行多个任务之间,可能会有某个 \(a_i\) 发生改变。

他的任务总数与改变某个 \(a_i\) 的次数之和为 \(m\)。


WGY:LCT

这道题的题意是真的绕,我前前后后读了不下五遍才大概意会。。。

做数据结构的题一般都要先浓缩题意。这道题是让我们求后缀积之和,再加上修改操作就基本上确定用LCT了

需要维护的信息有前缀积之和,后缀积之和,区间乘法三个信息。

对于修改操作,直接上LCT的套路makeroot和access。

对于询问操作,我们直接按照LCT的套路用makeroot和access把链给搞出来。

其实这就相当于一道LCT的板题吧。。。全部都是基础操作。。。

完整代码在这里Link

如果链接失效了就这个Link

const int SIZE = 1e5 + 5;
const int M_SIZE = 2e5 + 5;
const int MOD = 20924;
int head[M_SIZE], nxt[M_SIZE];
int to[M_SIZE], ints[SIZE];
int waste[SIZE], n, m, top, tot;
struct SPLAY {
int fa;
int ch[2];
int prod;
int preprod;
int sufprod;
int lztg;
} data[SIZE]; void AddEdge(int x, int y) {
to[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
} bool IsRoot(int x) {
return ((data[data[x].fa].ch[1] ^ x) && (data[data[x].fa].ch[0] ^ x));
} bool WhichSon(int x) {
return (data[data[x].fa].ch[1] == x);
} void UpdateMessages(int x) {
data[x].prod = data[data[x].ch[0]].prod * data[data[x].ch[1]].prod % MOD * ints[x] % MOD;
data[x].preprod = (data[data[x].ch[0]].preprod + data[data[x].ch[0]].prod * ints[x] + data[data[x].ch[0]].prod * ints[x] % MOD * data[data[x].ch[1]].preprod) % MOD;
data[x].sufprod = (data[data[x].ch[1]].sufprod + data[data[x].ch[1]].prod * ints[x] + data[data[x].ch[1]].prod * ints[x] % MOD * data[data[x].ch[0]].sufprod) % MOD;
} void UpdateSons(int x) {
if (data[x].lztg) {
swap(data[data[x].ch[0]].ch[0], data[data[x].ch[0]].ch[1]);
swap(data[data[x].ch[1]].ch[0], data[data[x].ch[1]].ch[1]);
swap(data[data[x].ch[0]].preprod, data[data[x].ch[0]].sufprod);
swap(data[data[x].ch[1]].preprod, data[data[x].ch[1]].sufprod);
data[data[x].ch[0]].lztg ^= 1;
data[data[x].ch[1]].lztg ^= 1;
data[x].lztg = 0;
}
} void RotateNode(int x) {
int y = data[x].fa;
int z = data[y].fa;
int k = WhichSon(x);
if (!IsRoot(y)) data[z].ch[WhichSon(y)] = x;
data[y].fa = x;
data[data[y].fa].fa = z;
if (data[x].ch[k ^ 1]) data[data[x].ch[k ^ 1]].fa = y;
data[y].ch[k] = data[x].ch[k ^ 1];
data[x].ch[k ^ 1] = y;
UpdateMessages(y);
} void SplayToRoot(int x) {
int y = waste[top = 1] = x;
while (!IsRoot(y)) waste[++top] = y = data[y].fa;
while (top) UpdateSons(waste[top--]);
for (; !IsRoot(x); RotateNode(x))
if (!IsRoot((y = data[x].fa)))
RotateNode((data[data[y].fa].ch[1] ^ y ^ data[y].ch[1] ^ x) ? x : y);
UpdateMessages(x);
} void AccessEdge(int x) {
for (int y = 0; x; x = data[y = x].fa) {
SplayToRoot(x);
data[x].ch[1] = y;
UpdateMessages(x);
}
} void MakeRoot(int x) {
AccessEdge(x);
SplayToRoot(x);
swap(data[x].ch[0], data[x].ch[1]);
swap(data[x].preprod, data[x].sufprod);
data[x].lztg ^= 1;
UpdateMessages(x);
} void SplitTree(int x, int y) {
MakeRoot(x);
AccessEdge(y);
SplayToRoot(y);
} void Prepare(int x) {
data[x].prod = ints[x];
data[x].preprod = ints[x];
data[x].sufprod = ints[x];
for (int i = head[x]; i; i = nxt[i])
if (to[i] ^ data[x].fa)
data[to[i]].fa = x, Prepare(to[i]);
} int GetAnswers(int x, int y) {
SplitTree(x, y);
UpdateSons(y);
return data[y].sufprod;
} void Behavior(int x, int y) {
SplitTree(x, x);
ints[x] += y;
ints[x] %= MOD;
data[x].prod = ints[x];
data[x].preprod = ints[x];
data[x].sufprod = ints[x];
} signed main() {
read(n, m);
for (int i = 1; i <= n; ++i) read(ints[i]);
for (int i = 1, x, y; i < n; ++i) read(x, y), AddEdge(x, y), AddEdge(y, x);
(*data).prod = 1;
Prepare(1);
for (int i = 0, x, y; i < m; ++i) {
char opt[5];
read(opt);
read(x, y);
if (*opt ^ 'C') write(io_l, GetAnswers(x, y));
else Behavior(x, y);
}
return 0;
}

LYC:TCS

// 无情的LYC咕掉了这篇题解,快去打他!!!

30.P5350 序列

有一个序列\(a_n\)和若干操作。

  • \(\mathrm{1\ l \ r \ }\) 求\(a_l\)到\(a_r\)的和
  • \(\mathrm{2\ l \ r \ val \ }\) 将\(a_l\)到\(a_r\)赋值为\(\mathrm{val}\)
  • \(\mathrm{3\ l \ r \ val\ }\)将\(a_l\)到\(a_r\)加上\(\mathrm{val}\)
  • \(\mathrm{4\ l_1 \ r_1 \ l_2 \ r_2 }\)将\(a_{l_1}\)到\(a_{r_1}\)复制到\(a_{l_2}\)到\(a_{r_2}\)处
  • \(\mathrm{5\ l_1 \ r_1 \ l_2 \ r_2 }\)将\(a_{l_1}\)到\(a_{r_1}\)与\(a_{l_2}\)到\(a_{r_2}\)交换
  • \(\mathrm{6\ l \ r \ }\)将\(a_l\)到\(a_r\)翻转

这道题有了区间推平基本就没什么悬念了。ODT,上!(雾

具体讲一下吧

求和操作、区间赋值操作、区间加法没什么技术含量,数据结构100题里有ODT,请自行翻阅。

区间反转也很显然,不再赘述。

复制区间操作,我们直接split出l1,r1,l2,l2,然后把复制看作删除l2,r2,把l1,r1放在上面。即l1,r1,val --> l2-l1+l,l2-l1+r,val。

交换区间操作,本质就是复制区间操作做两次。

Code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set> using namespace std;
typedef long long LL; const int SIZE = 5e5 + 5;
const int MOD = 1e9 + 7; char buf[1 << 21], *p1 = buf, *p2 = buf;
#ifndef ONLINE_JUDGE
#define gc() getchar()
#else
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
#endif
#define is_number (ch >= '0' && ch <= '9') template < typename Type >
void read(Type& a) {
a = 0; bool f = 0; char ch;
while (!(ch = gc(), is_number)) if (ch == '-') f = 1;
while (is_number) a = (a << 3) + (a << 1) + (ch ^ '0'), ch = gc();
a = (f ? -a : a);
} template < typename Type, typename... Args >
void read(Type& t, Args&... args) {
read(t), read(args...);
} struct CHTHOLLY {
int l, r;
mutable LL val;
friend bool operator < (const CHTHOLLY& x, const CHTHOLLY& y) {
return x.l < y.l;
}
} a[SIZE], b[SIZE];
int n, q;
set < CHTHOLLY > st;
#define IT set < CHTHOLLY >::iterator void split(int l, int r) {
IT it = --st.upper_bound({l, l, 0});
CHTHOLLY t = *it;
if ((*it).l != l) st.erase(it), st.insert({t.l, l - 1, t.val}), st.insert({l, t.r, t.val});
it = --st.upper_bound({r, r, 0});
t = *it;
if ((*it).r != r) st.erase(it), st.insert({t.l, r, t.val}), st.insert({r + 1, t.r, t.val});
} void assign(int l, int r, int key) {
split(l, r);
st.erase(st.lower_bound({l, l, 0}), st.upper_bound({r, r, 0}));
st.insert({l, r, key});
} int queryf(int l, int r) {
LL res = 0;
split(l, r);
IT it = st.lower_bound({l, l, 0});
for (; it != st.end() && (*it).r <= r; ++it) res = (res + (*it).val * ((*it).r - (*it).l + 1)) % MOD;
return res;
} void update(int l, int r, int key) {
split(l, r);
IT it = st.lower_bound({l, l, 0});
for (; it != st.end() && (*it).r <= r; ++it) (*it).val = ((*it).val + key) % MOD;
} void copy(int l1, int r1, int l2, int r2) {
split(l1, r1);
split(l2, r2);
st.erase(st.lower_bound({l2, l2, 0}), st.upper_bound({r2, r2, 0}));
IT it = st.lower_bound({l1, l1, 0});
for (; it != st.end() && (*it).r <= r1; ++it) st.insert({l2 + (*it).l - l1, l2 + (*it).r - l1, (*it).val});
} void exchange(int l1, int r1, int l2, int r2) {
split(l1, r1);
split(l2, r2);
IT it = st.lower_bound({l1, l1, 0});
int lenx = 0, leny = 0;
for (; it != st.end() && (*it).r <= r1; ++it) a[++lenx] = *it;
it = st.lower_bound({l2, l2, 0});
for (; it != st.end() && (*it).r <= r2; ++it) b[++leny] = *it;
st.erase(st.lower_bound({l1, l1, 0}), st.upper_bound({r1, r1, 0}));
st.erase(st.lower_bound({l2, l2, 0}), st.upper_bound({r2, r2, 0}));
for (int i = 1; i <= lenx; ++i) st.insert({l2 - l1 + a[i].l, l2 - l1 + a[i].r, a[i].val});
for (int i = 1; i <= leny; ++i) st.insert({l1 - l2 + b[i].l, l1 - l2 + b[i].r, b[i].val});
} void reverse(int l, int r) {
split(l, r);
IT it = st.lower_bound({l, l, 0});
int lim = 0;
for (; it != st.end() && (*it).r <= r; ++it) a[++lim] = *it;
st.erase(st.lower_bound({l, l, 0}), st.upper_bound({r, r, 0}));
for (int i = lim; i; --i) st.insert({l + r - a[i].r, l + r - a[i].l, a[i].val});
} void Imomonalse() {
int opt, l, r, l0, r0, key;
read(opt, l, r);
if (opt == 1) printf("%d\n", queryf(l, r));
else if (opt == 2) read(key), assign(l, r, key);
else if (opt == 3) read(key), update(l, r, key);
else if (opt == 4) read(l0, r0), copy(l, r, l0, r0);
else if (opt == 5) read(l0, r0), exchange(l, r, l0, r0);
else if (opt == 6) reverse(l, r);
} void init() {
read(n, q);
for (int i = 1, x; i <= n; ++i) read(x), st.insert({i, i, x});
for (int i = 0; i < q; ++i) Imomonalse();
} void write() {
IT it = st.begin();
for (; it != st.end(); ++it)
for (int i = (*it).l; i <= (*it).r; ++i)
printf("%lld ", (*it).val);
} signed main() {
init();
write();
}

Ds100p -「数据结构百题」21~30的更多相关文章

  1. LibreOJ #6007. 「网络流 24 题」方格取数 最小割 最大点权独立集 最大流

    #6007. 「网络流 24 题」方格取数 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   题目描述 ...

  2. Libre 6009 「网络流 24 题」软件补丁 / Luogu 2761 软件安装问题 (最短路径,位运算)

    Libre 6009 「网络流 24 题」软件补丁 / Luogu 2761 软件安装问题 (最短路径,位运算) Description T 公司发现其研制的一个软件中有 n 个错误,随即为该软件发放 ...

  3. LOJ6003 - 「网络流 24 题」魔术球

    原题链接 Description 假设有根柱子,现要按下述规则在这根柱子中依次放入编号为的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何2个相邻球的编号之和为完全平方数. 试设计一个算法 ...

  4. LOJ6002 - 「网络流 24 题」最小路径覆盖

    原题链接 Description 求一个DAG的最小路径覆盖,并输出一种方案. Solution 模板题啦~ Code //「网络流 24 题」最小路径覆盖 #include <cstdio&g ...

  5. LOJ6001 - 「网络流 24 题」太空飞行计划

    原题链接 Description 有个实验和个仪器,做实验有报酬买仪器有花费.每个实验都需要一些仪器,求最大净收益(实验报酬仪器花费),并输出一组方案. Solution 实验向所需仪器连边,实验的点 ...

  6. LOJ6000 - 「网络流 24 题」搭配飞行员

    原题链接 题意简述 求二分图的最大匹配. 题解 这里写的是匈牙利算法. 表示节点的当前匹配. 为真表示在这一轮匹配中,无法给节点一个新的匹配.所以如果为真就不用再dfs它了,直接continue就好. ...

  7. LibreOJ #6014. 「网络流 24 题」最长 k 可重区间集

    #6014. 「网络流 24 题」最长 k 可重区间集 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   ...

  8. Libre 6013 「网络流 24 题」负载平衡 (网络流,最小费用最大流)

    Libre 6013 「网络流 24 题」负载平衡 (网络流,最小费用最大流) Description G 公司有n 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等.如何用最少搬运量可以使n ...

  9. Libre 6012 「网络流 24 题」分配问题 (网络流,费用流)

    Libre 6012 「网络流 24 题」分配问题 (网络流,费用流) Description 有n件工作要分配给n个人做.第i个人做第j件工作产生的效益为\(c_{ij}\).试设计一个将n件工作分 ...

  10. Libre 6011 「网络流 24 题」运输问题 (网络流,最小费用最大流)

    Libre 6011 「网络流 24 题」运输问题 (网络流,最小费用最大流) Description W 公司有m个仓库和n个零售商店.第i个仓库有\(a_i\)个单位的货物:第j个零售商店需要\( ...

随机推荐

  1. Anaconda 环境下 R 包 ggraph_1.0.2 安装小记

    由于微信不允许外部链接,你需要点击文章尾部左下角的 "阅读原文",才能访问文中链接. 记录一下今天在 Anaconda3 环境下 R==3.4.3 中安装 ggraph 的一些问题 ...

  2. 2023-06-07:Redis 持久化方式有哪些?以及有什么区别?

    2023-06-07:Redis 持久化方式有哪些?以及有什么区别? 答案2023-06-07: Redis提供了两种持久化机制:RDB和AOF. RDB RDB持久化是将Redis当前进程中的数据生 ...

  3. JuiceFS 社区版 v1.1- Beta 发布,新增五个实用功能

    我们很高兴地宣布 JuiceFS v1.1-Beta 版本正式发布啦!这是一个功能丰富的版本,带来了许多实用的新功能和改进.在这个版本中我们新增了以下功能: 目录配额:为目录设置配额限制,控制其大小和 ...

  4. 【python基础】input函数

    1.初识input函数 大多数程序都旨在解决最终用户的问题,为此通常需要从用户那里获取一些信息.例如假设有人要判断自己是否到了投票的年龄,要编写回答这个问题的程序,就需要知道用户的年龄,这样才能给出答 ...

  5. 基于Node.js的分布式应用程序架构设计与最佳实践:实现高效、可扩展的分布式系统

    目录 基于Node.js的分布式应用程序架构设计与最佳实践:实现高效.可扩展的分布式系统 随着互联网的普及和发展,分布式系统已经成为现代应用程序中不可或缺的一部分.而Node.js作为当前最流行的Ja ...

  6. Educational Codeforces Round 150 (Rated for Div. 2) A-E

    比赛链接 A 代码 #include <bits/stdc++.h> using namespace std; using ll = long long; bool solve() { i ...

  7. 基于Surprise协同过滤实现短视频推荐

    ​ 前言 前面一文介绍了通过基础的web项目结构实现简单的内容推荐,与其说那个是推荐不如说是一个排序算法.因为热度计算方式虽然解决了内容的时效质量动态化.但是相对用户而言,大家看到的都是几乎一致的内容 ...

  8. 国产开源流批统一的数据同步工具Chunjun入门实战

    @ 目录 概述 定义 特性 部署 安装 版本对应关系 通用配置详解 整体配置 Content 配置 Setting 配置 Local提交 Standalone提交 Json方式使用 SQL方式使用 M ...

  9. Kali下载安装以及基础配置

    Kali官网:Kali Linux | Penetration Testing and Ethical Hacking Linux Distribution Kali下载地址:Get Kali | K ...

  10. ListView选中获取数据并弹出菜单项

    前言 作为一名Android小白,我在编写过程中,使用ListView列表,想要使用他来完成长按弹出菜单选项,并且还要进行事件操作,经过百度编程的经历后,终于成功完成.在此附上这块比较完整的代码,理论 ...