第一场春训,谢谢lzy、pmxm、jsh等大大 qwq!
谨以E题纪念我的第一次树链剖分…
【BUAA Spring Training 01】
Tags:DAG最长路(拓扑排序) 离散化 堆 并查集 平衡树 线段树
Problem A. 括号序列计分
[A] 题意
给定一个保证正常匹配的小括号序列(长度 ∈ [ 1 , 100000 ] \in [1, 100000] ∈[1,100000])。定义得分:
- ( ) () () 的得分是 1
- 若 A A A 是非空括号序列,得分是 k k k,则 ( A ) (A) (A) 得分是 2 × k 2 \times k 2×k
- 若 A 、 B A、B A、B 都是非空括号序列,得分分别是 k a 、 k b k_a、k_b ka、kb,则 A B AB AB 得分是 k a + k b k_a+k_b ka+kb
输出该序列的得分(对 12345678910 12345678910 12345678910 取模)
[A] 思路
递归定义,递归解决。
时间复杂度: O ( n ) O(n) O(n)
[A] 代码
/*
* If we give,
* all we've got,
* we will make it through.
*/
#include <cstdio>
#define LL long long
constexpr int MN(1e5+7);
constexpr LL MOD(12345678910LL);
char s[MN], *now = s;
LL get_term(void);
LL get_expr(void);
int main()
{
int N, tp;
scanf("%d", &N);
for (int i=0; i<N; ++i)
{
scanf("%d", &tp);
s[i] = tp ? ')' : '(';
}
printf("%lld", get_expr());
return 0;
}
LL get_term(void)
{
LL ret = 0;
if (*now != '(')
return ret;
++now, ret = get_expr(), ++now;
return ret ? (ret << 1) % MOD : 1;
}
LL get_expr(void)
{
LL sum = 0;
while (*now == '(')
sum = (sum + get_term() % MOD) % MOD;
return sum;
}
Problem B. 冰镇矩阵
[B] 题意
给定一个 R R R 行 C C C 列( R × C ∈ [ 1 , 100000 ] R \times C \in [1, 100000] R×C∈[1,100000])的矩阵,
要把这个矩阵的值离散化,具体要求:
- 同行 or 同列中相等的值在离散化后仍然相等
- 同行 or 同列中不等的值之间的不等关系在离散化后仍然保持
输出离散化后的矩阵中的最大值。
[B] 思路
其实就是求一个不等关系最长链。对应解法就是先把逐行把同行中的相等的值对应的结点给连起来(相当于缩点。利用并查集即可),然后逐列作相同处理。
然后开始建图。先逐行遍历,把缩点后的、具有不等关系的结点之间连一条有向边。然后逐列作相同处理。
然后就在建出来的 DAG 上跑最长路(拓扑排序即可)。最长路上的结点数就是答案。
时间复杂度: O ( ( n ∗ m ) log ( n ∗ m ) ) O((n*m)\log(n*m)) O((n∗m)log(n∗m))
然鹅逐行排序、逐列排序真的好烦啊… (于是跑去CF看了下前几名的解法 qwq)发现了大神们的另一种解法:直接全员排序,然后贪心地从全局最小值开始考虑,直到最大值。当然,答案显然不是通过简单的 sort->unique->erase 就能得到的,而还是要用并查集维护一下同行同列相同的值。
时间复杂度: O ( ( n ∗ m ) log ( n ∗ m ) ) O((n*m)\log(n*m)) O((n∗m)log(n∗m))
[B] 代码
法一,建图跑拓扑排序:
#include <bits/stdc++.h>
#define GC getchar()
#define _SN(x) {char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define _SAN(a,n) {auto _i=0,_n=n;for(;_i<_n;++_i)_SN(a[_i])}
#define _SA(a,l,r) {auto _i=l,_r=r;for(;_i<_r;++_i)_SN(a[_i])}
#define _gS(_1, _2, _3, _sc, ...) _sc
#define sc(...) _gS(__VA_ARGS__,_SA,_SAN,_SN, ...)(__VA_ARGS__)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define _F0N(i,n) for(auto i=0;i<n;++i)
#define _FLR(i,l,r) for(auto i=l,_r=r;i<_r;++i)
#define _gF(_1, _2, _3, _F, ...) _F
#define F(...) _gF(__VA_ARGS__,_FLR,_F0N, ...)(__VA_ARGS__)
#define _FD0(i,n) for(auto i=0;i<=n;++i)
#define _FDL(i,l,r) for(auto i=l,_r=r;i<=_r;++i)
#define _gFD(_1, _2, _3, _FD, ...) _FD
#define FD(...) _gFD(__VA_ARGS__,_FDL,_FD0, ...)(__VA_ARGS__)
#define FED(src) for (Ed *p=head[src]; p; p=p->next)
#define OPER1(T,x1,b1) inline bool operator<(const T&_o)const{return x1 b1 _o.x1;}
#define OPER2(T,x1,b1,x2,b2) inline bool operator<(const T&_o)const{return x1 b1 _o.x1||x1==_o.x1&&x2 b2 _o.x2;}
#define OPER3(T,x1,b1,x2,b2,x3,b3) inline bool operator<(const T&_o)const{return x1 b1 _o.x1||x1==_o.x1&&(x2 b2 _o.x2||x2==_o.x2&&x3 b3 _o.x3);}
#define IL inline
#define LL long long
#define ULL unsigned LL
#define PC putchar
template<class T>
void PRT(const T _){if(_<0){PC(45),PRT(-_);return;}if(_>=10)PRT(_/10);PC(_%10+48);}
template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}
#define CON constexpr
#define T_CASE int T;sc(T)for(int CASE=1;CASE<=T;++CASE)
#define Tjj int T;sc(T)while(T--)
#define qjj int q;sc(q)while(q--)
#define cincout std::cin.tie(nullptr),std::cout.tie(nullptr),std::ios::sync_with_stdio(false);
#define EPS 1e-8
#define PI 3.141592653589793
#define MAX_INT 2147483647
#define MIN_INT -2147483648
#define MAX_LL 9223372036854775807LL
#define MIN_LL -9223372036854775808LL
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3fLL
#define priority_queue priority_queue
#define PQ std::priority_queue
#define PR std::pair
#define vector vector
#define unordered_ unordered_
#define VI std::vector<int>
#define MII std::map<int,int>
#define MLI std::map<LL,int>
#define MSI std::map<std::string,int>
#define PII std::pair<int,int>
#define PLI std::pair<LL,int>
#define PSI std::pair<std::string,int>
#define MPFD(k) auto it=mp.find(k)
#define MIN(a, b) ((a)<(b)?(a):(b))
#define MIN3(a, b, c) (MIN(a, MIN(b, c)))
#define MAX(a, b) ((a)>(b)?(a):(b))
#define MAX3(a, b, c) (MAX(a, MAX(b, c)))
#define ABS(a) ((a)>0?(a):-(a))
#define FABS(a) ((a)>0?(a):-(a))
#define log2n(x) (log(x)/0.69314718055995)
#define MHD(p1, p2) ((p1.x>p2.x?p1.x-p2.x:p2.x-p1.x)+(p1.y>p2.y?p1.y-p2.y:p2.y-p1.y))
#define PB emplace_back
#define EB emplace_back
#define BRK else break
#define ALL(X) (X).begin(),(X).end()
#define SORT(X) std::sort(ALL(X))
#define SORTD(X) std::sort(ALL(X),std::greater<decltype((X)[0])>())
#define SWAP(a, b) do{auto _t=a; a=b; b=_t;}while(0)
#define mem0(a) memset(a,0,sizeof(a))
#define memf1(a) memset(a,-1,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))
CON int MN(1e6+7), MV(MN), ME(MV << 1);
struct Elem
{
int val;
int id;
};
struct Iter
{
Elem *ptr;
inline Iter & operator =(Elem *ptr)
{
this->ptr = ptr;
return *this;
}
inline Elem * operator ->(void)
{
return ptr;
}
inline bool operator <(const Iter o) const
{
return ptr->val < o.ptr->val;
}
};
std::vector<Elem> a[MN];
Iter iter[MN];
namespace UF
{
int uf[MV];
inline void init(const int V)
{
memset(uf, -1, (V+1) * sizeof(*uf));
}
int find(const int x)
{
if (uf[x] >= 0)
return uf[x] = find(uf[x]);
return x;
}
inline void merge(int x, int y)
{
x = find(x), y = find(y);
if (x < y)
uf[x] += uf[y], uf[y] = x;
else if(y < x)
uf[y] += uf[x], uf[x] = y;
}
};
struct Ed
{
int v;
Ed *next;
} ed[ME], *head[MV];
int tot;
int ind[MV];
inline void edd(const int u, const int v)
{
ed[++tot].next = head[u],
ed[tot].v = v,
head[u] = ed+tot,
++ind[v];
}
struct Node
{
int v;
int dep;
} q[MV];
void merge_same_vals(const int R, const int C);
void build_graph(const int R, const int C);
int topo_sort(const int V);
int main()
{
get(R, C)
int cnt = 0;
F(r, R)
{
a[r].resize(C); // 不开O2的话,reserve更快,但有风险。reserve不改变.size(),意味着大小我们自己要知道。另外reserve不对allocate到的空间进行初始化。
F(c, C)
{
sc(a[r][c].val)
a[r][c].id = ++cnt;
}
}
merge_same_vals(R, C);
build_graph(R, C);
UPRT(topo_sort(R*C));
return 0;
}
void merge_same_vals(const int R, const int C)
{
UF::init(R*C);
F(r, R)
{
F(c, C)
iter[c] = &a[r][c];
std::sort(iter, iter+C); // 就地排序
F(c, 1, C)
if (iter[c-1]->val == iter[c]->val)
UF::merge(iter[c-1]->id, iter[c]->id); // 合并同行等值的
}
F(c, C)
{
F(r, R)
iter[r] = &a[r][c];
std::sort(iter, iter+R); // 就地排序
F(r, 1, R)
if (iter[r-1]->val == iter[r]->val)
UF::merge(iter[r-1]->id, iter[r]->id); // 合并同列等值的
}
}
void build_graph(const int R, const int C)
{
F(r, R)
{
F(c, C)
iter[c] = &a[r][c];
std::sort(iter, iter+C); // 就地排序
F(c, 1, C)
if (iter[c-1]->val != iter[c]->val)
edd(UF::find(iter[c-1]->id), UF::find(iter[c]->id)); // 建边
}
F(c, C)
{
F(r, R)
iter[r] = &a[r][c];
std::sort(iter, iter+R); // 就地排序
F(r, 1, R)
if (iter[r-1]->val != iter[r]->val)
edd(UF::find(iter[r-1]->id), UF::find(iter[r]->id)); // 建边
}
}
int topo_sort(const int V)
{
int hd = 0, tl = 0;
FD(v, 1, V)
if (UF::uf[v]<0 && !ind[v])
q[tl].v = v, q[tl++].dep = 1;
while (hd != tl)
{
const Node &now = q[hd++];
FED(now.v)
if (!--ind[p->v])
q[tl].v = p->v, q[tl++].dep = now.dep+1;
}
return q[tl-1].dep;
}
法二:贪心,直接维护并查集
#include <bits/stdc++.h>
#define GC getchar()
#define _SN(x) {char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define _SAN(a,n) {auto _i=0,_n=n;for(;_i<_n;++_i)_SN(a[_i])}
#define _SA(a,l,r) {auto _i=l,_r=r;for(;_i<_r;++_i)_SN(a[_i])}
#define _gS(_1, _2, _3, _sc, ...) _sc
#define sc(...) _gS(__VA_ARGS__,_SA,_SAN,_SN, ...)(__VA_ARGS__)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define _F0N(i,n) for(auto i=0;i<n;++i)
#define _FLR(i,l,r) for(auto i=l,_r=r;i<_r;++i)
#define _gF(_1, _2, _3, _F, ...) _F
#define F(...) _gF(__VA_ARGS__,_FLR,_F0N, ...)(__VA_ARGS__)
#define _FD0(i,n) for(auto i=0;i<=n;++i)
#define _FDL(i,l,r) for(auto i=l;i<=r;++i)
#define _gFD(_1, _2, _3, _FD, ...) _FD
#define FD(...) _gFD(__VA_ARGS__,_FDL,_FD0, ...)(__VA_ARGS__)
#define FED(src) for (Ed *p=head[src]; p; p=p->next)
#define OPER1(T,x1,b1) inline bool operator<(const T&_o)const{return x1 b1 _o.x1;}
#define OPER2(T,x1,b1,x2,b2) inline bool operator<(const T&_o)const{return x1 b1 _o.x1||x1==_o.x1&&x2 b2 _o.x2;}
#define OPER3(T,x1,b1,x2,b2,x3,b3) inline bool operator<(const T&_o)const{return x1 b1 _o.x1||x1==_o.x1&&(x2 b2 _o.x2||x2==_o.x2&&x3 b3 _o.x3);}
#define IL inline
#define LL long long
#define ULL unsigned LL
#define PC putchar
template<class T>
void PRT(const T _){if(_<0){PC(45),PRT(-_);return;}if(_>=10)PRT(_/10);PC(_%10+48);}
template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}
#define CON constexpr
#define T_CASE int T;sc(T)for(int CASE=1;CASE<=T;++CASE)
#define Tjj int T;sc(T)while(T--)
#define qjj int q;sc(q)while(q--)
#define cincout std::cin.tie(nullptr),std::cout.tie(nullptr),std::ios::sync_with_stdio(false);
#define EPS 1e-8
#define PI 3.141592653589793
#define MAX_INT 2147483647
#define MIN_INT -2147483648
#define MAX_LL 9223372036854775807LL
#define MIN_LL -9223372036854775808LL
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3fLL
#define priority_queue priority_queue
#define PQ std::priority_queue
#define PR std::pair
#define vector vector
#define unordered_ unordered_
#define VI std::vector<int>
#define MII std::map<int,int>
#define MLI std::map<LL,int>
#define MSI std::map<std::string,int>
#define PII std::pair<int,int>
#define PLI std::pair<LL,int>
#define PSI std::pair<std::string,int>
#define MPFD(k) auto it=mp.find(k)
#define MIN(a, b) ((a)<(b)?(a):(b))
#define MIN3(a, b, c) (MIN(a, MIN(b, c)))
#define MAX(a, b) ((a)>(b)?(a):(b))
#define MAX3(a, b, c) (MAX(a, MAX(b, c)))
#define ABS(a) ((a)>0?(a):-(a))
#define FABS(a) ((a)>0?(a):-(a))
#define log2n(x) (log(x)/0.69314718055995)
#define MHD(p1, p2) ((p1.x>p2.x?p1.x-p2.x:p2.x-p1.x)+(p1.y>p2.y?p1.y-p2.y:p2.y-p1.y))
#define PB emplace_back
#define EB emplace_back
#define BRK else break
#define ALL(X) (X).begin(),(X).end()
#define SORT(X) std::sort(ALL(X))
#define SORTD(X) std::sort(ALL(X),std::greater<decltype((X)[0])>())
#define SWAP(a, b) do{auto _t=a; a=b; b=_t;}while(0)
#define mem0(a) memset(a,0,sizeof(a))
#define memf1(a) memset(a,-1,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))
CON int MN(1e6+7), MV(MN), ME(MV << 1);
struct Elem
{
int val;
int r, c, pos;
int id;
} a[MN];
struct Iter
{
Elem *ptr;
inline Iter & operator =(Elem *ptr)
{
this->ptr = ptr;
return *this;
}
inline Elem * operator ->(void)
{
return ptr;
}
inline bool operator <(const Iter o) const
{
return ptr->val < o.ptr->val;
}
} iter[MN];
namespace UF
{
int uf[MV];
int find(const int x)
{
return uf[x]==x ? x : uf[x]=find(uf[x]);
}
};
int last_r_pos[MN];
int last_c_pos[MN];
int main()
{
_IO
get(R, C)
int tot = 0;
FD(r, 1, R)
{
FD(c, 1, C)
{
++tot;
sc(a[tot].val)
a[tot].r = r, a[tot].c = c, a[tot].pos = UF::uf[tot] = tot,
iter[tot] = a+tot;
}
}
std::sort(iter+1, iter+tot+1);
int id1, id2, max_id = 0;
FD(i, 1, tot)
{
Iter p = iter[i];
const int last_r_p = UF::find(last_r_pos[p->c]); // 在第c行相同的数中,最靠后的位置是last_r_p(其具有最新的id)
const int last_c_p = UF::find(last_c_pos[p->r]); // 在第r列相同的数中,最靠后的位置是last_c_p(其具有最新的id)
if (p->val > a[last_r_p].val)
id1 = a[last_r_p].id + 1;
else
{
id1 = a[last_r_p].id;
if (p->val == a[last_r_p].val)
UF::uf[last_r_p] = p->pos; // 合并同行的等值点。只有把p->pos设为根结点,才能保证等值点的集合的根是pos最大者
}
if (p->val > a[last_c_p].val)
id2 = a[last_c_p].id + 1;
else
{
id2 = a[last_c_p].id;
if (p->val == a[last_c_p].val)
UF::uf[last_c_p] = p->pos; // 合并同列的等值点。只有把p->pos设为根结点,才能保证等值点的集合的根是pos最大者
}
p->id = MAX(id1, id2);
max_id = MAX(p->id, max_id);
last_r_pos[p->c] = last_c_pos[p->r] = p->pos; // 保存刚才在第c行、第r列填的位置(显然这个位置不一定是与该pos值相同的、在同行or同列的数的集合中,位置最靠后的。所以才需要并查集)
// 存这个值是为了通过并查集能找到与该pos值相同的、在同行or同列的数的集合中,位置最靠后的在哪
}
UPRT(max_id);
// for (int i=1; i<=tot; PC(10))
// for (int c=0; c<C; ++c, ++i)
// UPRT(a[UF::find(i)].id), PC(32);
return 0;
}
Problem C. 斯波利特平衡术
[C] 题意
给定序列的长度 n n n,初始时整个序列是 [ 1 , 2 , . . . , n ] [1, 2, ..., n] [1,2,...,n]
有 k k k 次操作,每次操作给出两个整数 l , r l, r l,r,然后将此区间左右翻转。
要求每次操作后输出该区间的数之和,然后所有操作完成后输出最后的区间。
范围: 1 ≤ n ≤ 1 0 5 , 1 ≤ k ≤ 1 0 5 , 1 ≤ l ≤ r ≤ n 1\le n\le 10^5, 1\le k\le 10^5,1\le l \le r\le n 1≤n≤105,1≤k≤105,1≤l≤r≤n
[C] 思路
FHQ Treap / Splay 模板题…(本渣也是刚学w )
关于 FHQ Treap 的详解,可以戳这里:【平衡树总结Ⅲ】【FHQ Treap】非旋Treap | E
[C] 代码
#include <cstdio>
#include <random>
constexpr int MN(1e5+7);
template <typename vint, typename sint>
class FHQ
{
private:
using rint = unsigned long;
using xint = int;
std::mt19937 rander;
struct Node
{
Node *lc, *rc;
vint v;
sint s;
rint w;
xint sz;
bool reved;
void rev(void)
{
reved = !reved;
Node *tp = lc;
lc = rc;
rc = tp;
}
} _pool[MN], *pool=_pool;
Node _NIL = {&_NIL, &_NIL, 0, 0, 0, 0, false}, *const NIL = &_NIL;
Node *root = NIL;
Node *new_node(const vint v)
{
return *++pool = {NIL, NIL, v, v, rander(), 1, false}, pool;
}
#define pu(p) \
({ \
p->s = p->lc->s + p->rc->s + p->v, \
p->sz = p->lc->sz + p->rc->sz + 1; \
})
#define pd(p) \
({ \
if (p->reved) \
{ \
p->lc->rev(), p->rc->rev(), \
p->reved = false; \
} \
})
void mg(Node *&rt, Node *l, Node *r)
{
if (l == NIL)
rt = r;
else if (r == NIL)
rt = l;
else if (l->w < r->w)
rt = l, pd(rt), mg(rt->rc, rt->rc, r), pu(rt);
else
rt = r, pd(rt), mg(rt->lc, l, rt->lc), pu(rt);
}
void sp(Node *rt, Node *&l, Node *&r, const xint k)
{
if (rt == NIL)
l = r = NIL;
else if (k == 0)
l = NIL, r = rt;
else if (rt->lc->sz+1 <= k)
l = rt, pd(rt), sp(rt->rc, rt->rc, r, k-rt->lc->sz-1), pu(rt);
else
r = rt, pd(rt), sp(rt->lc, l, rt->lc, k), pu(rt);
}
public:
FHQ(void) : rander(19937) { }
void clear(void)
{
pool = _pool;
root = NIL;
}
void insert(const vint v)
{
mg(root, root, new_node(v));
}
sint rev(xint l, xint r)
{
Node *x, *y, *z;
sp(root, x, y, l-1);
sp(y, y, z, r-l+1);
// roots: x y z
// intervals: [ 1, l-1 ] [ l, r ] [ r+1, n ]
// size: l-1 r-l+1 n-r
y->rev();
sint s = y->s;
mg(y, y, z);
mg(root, x, y);
return s;
}
void dfs(Node *p)
{
if (p == NIL)
return;
pd(p);
dfs(p->lc), printf("%d ", p->v), dfs(p->rc);
}
void dfs(void)
{
dfs(root);
}
};
FHQ<int, long long> h;
int main()
{
int n, k, l, r;
scanf("%d %d", &n, &k);
for (int i=1; i<=n; ++i)
h.insert(i);
while (k--)
{
scanf("%d %d", &l, &r);
printf("%lld\n", h.rev(l, r));
}
h.dfs();
return 0;
}
Problem D. 前 k 小和
Problem E. npm - Node.js
[E] 题意
给定一棵根结点编号为 0 0 0 的树。初始点权都是 0 0 0,有两种操作:
- ① 查询结点到根的路径上有多少个值为 0 0 0 的点,然后路径上点权全部赋 1 1 1
- ② 查询某个结点的子树有多少个值为 1 1 1 的点,然后子树结点点权全部赋 0 0 0
范围: 1 ≤ n ≤ 1 0 5 , 1 ≤ q ≤ 1 0 5 1\le n\le 10^5, 1\le q\le 10^5 1≤n≤105,1≤q≤105
[E] 思路
树剖 模板题…(可是本渣才刚学这个w )
树剖就相当于是把一棵树给重新编号(重儿子优先的DFS序),然后再拿一棵线段树去维护这棵树的点权(实际上是维护一条条剖出来的重链,因为同一条链上的编号是连续的(优先遍历重儿子)。当然同时也可以顺带维护子树,因为子树的DFS序也是连续的)
因此我们就可以在 O ( l o g 2 ( n ) ) O(log^2(n)) O(log2(n)) 内完成每次树上简单路径点权更新/查询,或在 O ( l o g ( n ) ) O(log(n)) O(log(n)) 内完成子树点权更新/查询 了。
注意在处理链的时候,如果两个点不在一条重链上,那就得跳跃几次。每次跳的时候应该是更低的先跳,这样才能缩短他们的距离,或者说才能尽可能快地跳到他们的最近公共重链祖先上。
是不是很简单呢?就是代码量稍微有点大…
哦对了还有一点,就是这里有区间赋 0 0 0 操作,所以线段树懒标记得改一改。懒标记是 − 1 -1 −1 的时候才认为是无效的哦。
[E] 代码
/*
* If we give,
* all we've got,
* we will make it through.
*/
#include <cstdio>
#include <cstdlib>
#include <climits>
#define GC getchar()
#define _SN(x) {char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define _SAN(a,n) {auto _i=0,_n=n;for(;_i<_n;++_i)_SN(a[_i])}
#define _SA(a,l,r) {auto _i=l,_r=r;for(;_i<_r;++_i)_SN(a[_i])}
#define _gS(_1, _2, _3, _sc, ...) _sc
#define sc(...) _gS(__VA_ARGS__,_SA,_SAN,_SN, ...)(__VA_ARGS__)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define F(i, l, r) for(int i=l; i<r; ++i)
#define IL inline
#define PC putchar
template<class T>
void PRT(const T _){if(_<0){PC(45),PRT(-_);return;}if(_>=10)PRT(_/10);PC(_%10+48);}
template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}
#define CON constexpr
#define qjj int q;sc(q)while(q--)
#define SWAP(a, b) do{auto _t=a; a=b; b=_t;}while(0)
CON int MN(1e5+7);
CON int MV(MN), ME(MV*2);
struct Edge
{
int next, v;
} ed[ME];
int head[MV], tot;
#define edd(uu, vv) ed[++tot].next=head[uu], ed[tot].v=vv, head[uu]=tot
CON int ROOT(0);
int de[MV], fa[MV], sz[MV], son[MV];
void dfs1(const int u)
{
int msz = -1;
for (int i=head[u]; i; i=ed[i].next)
{
const int v = ed[i].v;
if (v != fa[u])
{
de[v] = de[u] + 1,
fa[v] = u,
sz[v] = 1,
dfs1(v),
sz[u] += sz[v];
if (sz[v] > msz)
{
msz = sz[v],
son[u] = v;
}
}
}
}
int a0[MV]; // 原来树结点的点权(本题全是0)
int top[MV], id[MV], dnt;
int a[MV]; // 线段树要维护的点权
void dfs2(const int u)
{
if (son[u])
{
const int v = son[u];
top[v] = top[u], // 一脉相承
id[v] = ++dnt,
a[dnt] = a0[v],
dfs2(v);
}
for (int i=head[u]; i; i=ed[i].next)
{
const int v = ed[i].v;
if (v != fa[u] && v != son[u])
{
top[v] = v, // v是新链的根
id[v] = ++dnt,
a[dnt] = a0[v],
dfs2(v);
}
}
}
template <typename vint, typename sint, typename xint = int>
class STree
{
#define MMAX INT_MAX
#define MMIN INT_MIN
private:
static constexpr int ROOT = 1;
struct Node
{
xint l, r;
sint s, lz; // 【注意】本题懒标记为0的时候也是有意义的(区间赋0),故应把懒标记的无效值设为-1
} t[MN << 2];
vint *a;
xint ll, rr;
vint vv;
// 计算管辖区间中点,以及左右儿子
#define get_mlr(i) \
xint mid = (t[i].l+t[i].r) >> 1, li = i+i, ri = li+1
// 结点增值(不一定非得是叶节点哦)
#define set_v(i, v) \
do \
{ \
t[i].s = (sint)(v) * (t[i].r-t[i].l+1), \
t[i].lz = v; \
} while (0)
// 下传懒标记(向下更新/询问之前,要先下传懒标记哦)
#define push_down(i, li, ri) \
do \
{ \
sint lz = t[i].lz; \
if (~lz) \
{ \
set_v(li, lz); \
set_v(ri, lz); \
t[i].lz = -1; \
} \
} while (0)
// 用子结点的值求父节点的值(更新之后才需要push_up)
#define push_up(i, li, ri) \
do \
{ \
t[i].s = t[li].s + t[ri].s; \
} while (0)
void build(const xint i, const xint l, const xint r)
{
t[i].l = l, t[i].r = r, t[i].lz = -1;
if (l == r)
{
t[i].s = a[r];
}
else
{
get_mlr(i);
build(li, l, mid);
build(ri, mid+1, r);
push_up(i, li, ri); // 更新了,需要push_up
}
}
void set(const xint i)
{
if (ll <= t[i].l && t[i].r <= rr)
set_v(i, vv);
else
{
get_mlr(i);
push_down(i, li, ri);
if (ll <= mid) // 待更新区间和li管辖的区间有交集
set(li);
if (rr > mid) // 待更新区间和ri管辖的区间有交集
set(ri);
push_up(i, li, ri); // 更新了,需要push_up
}
}
sint sum(const xint i)
{
if (ll <= t[i].l && t[i].r <= rr)
return t[i].s;
else
{
get_mlr(i);
push_down(i, li, ri);
sint s = 0;
if (ll <= mid)
s += sum(li);
if (rr > mid)
s += sum(ri);
// 不用push_up,因为当初打懒标记的时候就已经更新了
return s;
}
}
public:
IL void build(vint *arr, const xint l, const xint r)
{
a = arr;
build(ROOT, l, r);
}
IL void set(const xint l, const xint r, const vint v)
{
ll = l, rr = r, vv = v,
set(ROOT);
}
IL sint sum(const xint l, const xint r)
{
ll = l, rr = r;
return sum(ROOT);
}
};
STree<int, int> st;
namespace SP
{
void set(int u, const int val)
{
st.set(id[u], id[u] + sz[u] - 1, val);
}
int sum(int u)
{
return st.sum(id[u], id[u] + sz[u] - 1);
}
void set(int u, int v, const int val)
{
while (top[u] != top[v])
{
if (de[top[u]] > de[top[v]])
SWAP(u, v);
st.set(id[top[v]], id[v], val);
v = fa[top[v]];
}
if (de[u] > de[v])
SWAP(u, v);
st.set(id[u], id[v], val);
}
int sum(int u, int v)
{
int s = 0;
while (top[u] != top[v])
{
if (de[top[u]] > de[top[v]])
SWAP(u, v);
s += st.sum(id[top[v]], id[v]);
v = fa[top[v]];
}
if (de[u] > de[v])
SWAP(u, v);
s += st.sum(id[u], id[v]);
return s;
}
}
int main()
{
#ifdef _VSC_KEVIN
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
get(V)
F(u, 1, V)
{
get(v)
edd(u, v);
edd(v, u);
}
de[ROOT] = 0, fa[ROOT] = ROOT, sz[ROOT] = 1;
dfs1(ROOT);
dnt = 0, top[ROOT] = ROOT, id[ROOT] = ++dnt, a[dnt] = a0[ROOT];
dfs2(ROOT);
st.build(a, 1, dnt);
qjj
{
get(op, u)
if (op) // install
{
UPRT((de[u] - de[ROOT] + 1) - SP::sum(u, ROOT)), PC(10);
SP::set(u, ROOT, 1);
}
else // uninstall
{
UPRT(SP::sum(u)), PC(10);
SP::set(u, 0);
}
}
return 0;
}
继续加油吧~ (期待认识即将一起奋斗的小伙伴们hhh)