【ADAFTBLL - Ada and Football】题解
Description
这题肯定是用树上带修莫队来做。
你很容易发现这和 糖果公园 一样,都是树上带修莫队。
Solution
所以就把树上莫队和带修莫队给合并起来就行了。
我猜你们一定已经学了带修莫队,这里就说一下树上莫队。
我们要先把原序列给变成欧拉序,把树上的点都变成序列,我们会得到一个长度为 \(2n\) 的序列,这就是欧拉序。
这样的话每个点肯定会被统计两次。
就像欧拉序是 12443321,每个点会统计两遍。
所以我们要判断是第一次统计还是第二次统计。
另外还有 \(LCA\) 的问题。
我们在预处理询问的时候,要求一遍 \(LCA\)
-
如果 \(LCA\) 等于左右两端点的一个的时候,就说明这两个端点实在同一个子树内部,这时候就不用求 \(LCA\),明显两个链上没有 \(LCA\)。
-
如果 \(LCA\) 不等于任何一个的时候,就要特判 \(LCA\),加上 \(LCA\) 的贡献。
修改和普通带修改莫队一样,只要加一维时间轴即可。
代码里面有一些注释。
Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int Maxk = 2e5 + 10;
int n,size,cnt,m,Q,cnts,cntq;
int st[Maxk],ed[Maxk],sum[Maxk];
int tot[Maxk],dfn[Maxk << 1],top[Maxk];
int deep[Maxk],fa[Maxk],used[Maxk];
int a[Maxk],son[Maxk],tmp[Maxk];
int head[Maxk],L[Maxk],R[Maxk],block[Maxk];
int ans[Maxk],Ans;
struct node{
int from;
int to;
int next;
}e[Maxk << 1];
struct Query{
int l;
int r;
int pos;
int nert;
int Lca_;
}s[Maxk];
struct Modify {
int past;
int Now;
}q[Maxk];
inline int read()
{
int x = 0,f = 0;char ch = getchar();
while(!isdigit(ch)) f |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48),ch = getchar();
return f ? -x : x;
}
void add_edge(int from,int to)//连边
{
e[++ cnt].to = to;
e[cnt].from = from;
e[cnt].next = head[from];
head[from] = cnt;
return;
}
void BLOCK()//分块
{
int nn = n << 1;
int len = pow(nn,0.6666667);
int T = nn / len;
for(int i = 1;i <= T;i ++) {
L[i] = (i - 1) * len + 1;
R[i] = i * len;
}
if(R[T] < nn) T ++,L[T] = R[T - 1] + 1,R[T] = nn;
for(int i = 1;i <= T;i ++)
for(int j = L[i];j <= R[i];j ++)
block[j] = i;
return;
}
void dfs1(int now,int f)//树链剖分常规操作
{
deep[now] = deep[f] + 1;
fa[now] = f;
st[now] = ++ size;
dfn[size] = now;
tot[now] = 1;
for(int i = head[now];i;i = e[i].next) {
int y = e[i].to;
if(y == f) continue;
dfs1(y,now);
tot[now] += tot[y];
if(tot[y] > tot[son[now]]) son[now] = y;
}
ed[now] = ++size;
dfn[size] = now;
}
void dfs2(int now,int topfa)
{
top[now] = topfa;
if(!son[now]) return ;
dfs2(son[now],topfa);
for(int i = head[now];i;i = e[i].next) {
int y = e[i].to;
if(y == fa[now] || y == son[now]) continue;
dfs2(y,y);
}
}
int LCA(int x,int y)//求出 LCA
{
while(top[x] ^ top[y]) {
if(deep[top[x]] < deep[top[y]]) swap(x,y);
x = fa[top[x]];
}
return deep[x] < deep[y] ? x : y;
}
bool CMP(Query x, Query y) { return block[x.l] == block[y.l] ? (block[x.r] == block[y.r] ? x.nert < y.nert : x.r < y.r) : x.l < y.l; }//排序
void Prepare()
{
for(int i = 1;i <= Q;i ++) {
int op = read(),x = read() + 1,y = read() + 1;
if(op == 1) {
q[++ cntq].past = x;
q[cntq].Now = y - 1;//修改操作
}
else {
if(st[x] > st[y]) swap(x,y);
int Lca = LCA(x,y);//求出 LCA
s[++ cnts].pos = cnts;
s[cnts].nert = cntq;
if(Lca == x) {//同子树内
s[cnts].l = st[x];
s[cnts].r = st[y];
}
else {
s[cnts].l = ed[x];//不同子树记得加上 LCA
s[cnts].r = st[y];
s[cnts].Lca_ = Lca;
}
}
}
return;
}
void del(int x)//删除操作
{
Ans -= sum[x] * (sum[x] - 1) * 1LL / 2;
sum[x] --;
Ans += sum[x] * (sum[x] - 1) * 1LL / 2;
}
void add(int x)//添加操作
{
Ans -= sum[x] * (sum[x] - 1) * 1LL / 2;
sum[x] ++;
Ans += sum[x] * (sum[x] - 1) * 1LL / 2;
}
void calc(int x)//判断应该删除这个元素还是加上
{
used[x] ? del(a[x]) : add(a[x]);
used[x] ^= 1;
}
void work(int now)//修改某个地方的值
{
if(used[q[now].past]) {
del(a[q[now].past]);
add(q[now].Now);
}
swap(a[q[now].past],q[now].Now);
}
void Solve()
{
sort(s + 1,s + cnts + 1,CMP);
int l = 1,r = 0,now = 0;
for(int i = 1;i <= cnts;i ++) {
int ql = s[i].l;
int qr = s[i].r;
while(l < ql) calc(dfn[l ++]);
while(l > ql) calc(dfn[-- l]);
while(r > qr) calc(dfn[r --]);
while(r < qr) calc(dfn[++ r]);
while(now < s[i].nert) work(++ now);
while(now > s[i].nert) work(now --);
if(s[i].Lca_) calc(s[i].Lca_);
ans[s[i].pos] = Ans;
if(s[i].Lca_) calc(s[i].Lca_);//加上 LCA 后记着还原
}
for(int i = 1;i <= cnts;i ++) printf("%lld\n",ans[i]);
return;
}
signed main()
{
n = read(),Q = read();
for(int i = 1;i <= n;i ++) a[i] = read();
for(int i = 1;i <= n - 1;i ++) {
int x = read() + 1,y = read() + 1;//记着连边 + 1
add_edge(x,y);
add_edge(y,x);
}
BLOCK();
dfs1(1,0);
dfs2(1,1);
Prepare();
Solve();
return 0;
}

浙公网安备 33010602011771号