Jc的宿舍
题解
忽然发现其他人好像都是用的树上莫队做的,只有我一个打了分块。
首先,如果你没有意识到这个强制在线是假的,或者你就要强制在线做的话,那应该不会去想树上莫队的解决方法。
既然不是树上莫队,那我们要怎么做呢?当然是平衡复杂度的算法啦。
首先我们可以发现一点,对于一个固定的序列,我们先让
T
i
T_i
Ti小的接再让
T
i
T_i
Ti大的接一定是最优的。
事实上,每个
T
i
T_i
Ti会对答案产生的贡献的系数是排在它后面的数的个数。
我们可以考虑对原序列进行值域分块,显然,在较小块中的点一定是比较大的块中的点先选的,也就是较大的块中每个点都会对较小块中每个点产生系数
1
1
1的贡献。
而块内自身还有相互的贡献需要处理。
事实上,对于一个块中,我们需要求出
3
3
3个东西,该块中数在路径上的点的数量,和,与其自身内部的相互贡献。
其关键还是求的该块中的数在路径上的点,而每个块在原树上的点都是
O
(
S
)
O\left(S\right)
O(S)级别的,实质上其不同的路径数是比较少的。
我们不妨考虑对其建出一棵虚树出来,对虚树上的每条路径预处理出来上面的这三个量。
对于前两个量,其实
b
f
s
bfs
bfs的过程中直接加起来就可以了,而对于第三个量,可以采用可持久化线段树比较简单地进行维护。每次更改相当于在原先的有序序列中插入一个数,直接加上这个数的影响就可以了。
由于我们每次只维护一个块中的数,其实我们可以将这个块中的数都先离散化后再插入到线段树中,这样我们线段树的下标范围就只用开到
S
S
S了。
这样的话,我们的预处理就是
O
(
n
S
log
S
)
O\left(nS\log S\right)
O(nSlogS)的。
而对于每次查询,我们相当于要对于每个块都找出该路径在虚其树上对应的链。
我们可以先预处理出来所有点在虚树上最近的祖先,对于这条路径,看它端点最近的两个祖先。
如果这两个点不是祖先关系,显然这条路径就是了,否则我们可以在虚树上倍增去找由于的链。
由于虚树只有
O
(
S
)
O(S)
O(S)的大小,虚树上倍增显然就是
O
(
log
S
)
O\left(\log S\right)
O(logS),比原树上倍增更优。
最后将不同块的答案组合起来即可。
总时间复杂度
O
(
n
S
log
S
+
n
m
log
S
S
)
⩾
O
(
n
m
log
n
)
O\left(nS\log S+\frac{nm\log S}{S}\right)\geqslant O\left(n\sqrt{m}\log n\right)
O(nSlogS+SnmlogS)⩾O(nmlogn)。
事实上有点卡常。
源码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define MAXN 50005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
typedef long double Ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int mod=1e5+7;
const int inv2=499122177;
const int jzm=2333;
const int zero=2000;
const int n1=150;
const int lim=10000000;
const int orG=3,ivG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-3;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0)putchar('-'),print(-x);if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
const int M=MAXN/n1+5,N=2*n1+5;
int n,m,key,val[MAXN],ord[MAXN],block[MAXN],L[MAXN],R[MAXN];
int dfn[MAXN],rd[MAXN],idx,dep[MAXN],root[N],ort[MAXN],b[MAXN],totb;
int sta[MAXN],stak,st[MAXN],stk,p[M][N],len[M];
int imp[MAXN],head[MAXN],tot,f[MAXN][20],ff[M][N][10],ip[M][MAXN];
LL dis[M][N][N],ct[M][N][N],sm[M][N][N],lastans;
bool vis[MAXN];vector<int>G[N];queue<int>q;
struct edge{int to,nxt;}e[MAXN<<1];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void AddEdge(int u,int v){G[u].pb(v);G[v].pb(u);}
bool cmp1(int x,int y){return val[x]<val[y];}
bool cmp2(int x,int y){return dfn[x]<dfn[y];}
void dosaka(int u,int fa){
dfn[u]=++idx;dep[u]=dep[fa]+1;f[u][0]=fa;
for(int i=1;i<16;i++)f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i;i=e[i].nxt)dosaka(e[i].to,u);
rd[u]=idx;
}
int lca(int a,int b){
if(dep[a]>dep[b])swap(a,b);
for(int i=15;i>=0;i--)if(dep[f[b][i]]>=dep[a])b=f[b][i];
if(a==b)return a;
for(int i=15;i>=0;i--)if(f[a][i]^f[b][i])a=f[a][i],b=f[b][i];
return f[a][0];
}
struct ming{int lson,rson,cnt;LL sum;};
class SegmentTree{
private:
ming tr[MAXN*25];int tott;
public:
void insert(int &now,int las,int l,int r,int ai){
if(l>r||l>ai||r<ai)return ;
tr[now=++tott]=tr[las];tr[now].cnt++;tr[now].sum+=b[ai];
if(l==r)return ;int mid=l+r>>1;
if(ai<=mid)insert(tr[now].lson,tr[las].lson,l,mid,ai);
if(ai>mid)insert(tr[now].rson,tr[las].rson,mid+1,r,ai);
}
int queryCnt(int rt,int l,int r,int al,int ar){
if(l>r||l>ar||r<al||al>ar||!rt)return 0;
if(al<=l&&r<=ar)return tr[rt].cnt;
int mid=l+r>>1,res=0;
if(al<=mid)res+=queryCnt(tr[rt].lson,l,mid,al,ar);
if(ar>mid)res+=queryCnt(tr[rt].rson,mid+1,r,al,ar);
return res;
}
LL querySum(int rt,int l,int r,int al,int ar){
if(l>r||l>ar||r<al||al>ar||!rt)return 0;
if(al<=l&&r<=ar)return tr[rt].sum;
int mid=l+r>>1;LL res=0;
if(al<=mid)res+=querySum(tr[rt].lson,l,mid,al,ar);
if(ar>mid)res+=querySum(tr[rt].rson,mid+1,r,al,ar);
return res;
}
void clear(){for(int i=1;i<=tott;i++)tr[i].lson=tr[i].rson=tr[i].cnt=tr[i].sum=0;tott=0;}
}T;
signed main(){
read(n);read(m);read(key);
for(int i=1;i<=n;i++)read(val[i]),ord[i]=i;
for(int i=1,x;i<=n;i++){read(x);if(x)addEdge(x,i);}
sort(ord+1,ord+n+1,cmp1);
for(int i=1;i<=n;i++)ort[ord[i]]=i;
for(int i=1;i<=n;i++)block[i]=(i+n1-1)/n1;
for(int i=1;i<=n;i++){if(!L[block[i]])L[block[i]]=i;R[block[i]]=i;}
dosaka(1,0);
for(int i=1;i<=block[n];i++){
stak=totb=0;for(int j=L[i];j<=R[i];j++)sta[++stak]=ord[j],b[++totb]=val[ord[j]];
sort(b+1,b+totb+1);totb=unique(b+1,b+totb+1)-b-1;
sort(sta+1,sta+stak+1,cmp2);int tsk=stak;
for(int j=1;j<tsk;j++)sta[++stak]=lca(sta[j],sta[j+1]);
sort(sta+1,sta+stak+1,cmp2);stak=unique(sta+1,sta+stak+1)-sta-1;stk=0;
for(int j=1;j<=stak;j++){
while(stk&&rd[sta[st[stk]]]<dfn[sta[j]])stk--;
if(stk)AddEdge(st[stk],j),ff[i][j][0]=st[stk];st[++stk]=j;
for(int k=1;k<9;k++)ff[i][j][k]=ff[i][ff[i][j][k-1]][k-1];
}
len[i]=stak;for(int j=1;j<=stak;j++)p[i][j]=sta[j];
for(int j=L[i];j<=R[i];j++)val[ord[j]]=lower_bound(b+1,b+totb+1,val[ord[j]])-b;
for(int j=1;j<=stak;j++){
while(!q.empty())q.pop();q.push(j);vis[j]=1;
if(L[i]<=ort[sta[j]]&&ort[sta[j]]<=R[i])
T.insert(root[j],0,0,totb,val[sta[j]]),
sm[i][j][j]=dis[i][j][j]=b[val[sta[j]]],ct[i][j][j]=1;
while(!q.empty()){
int u=q.front(),siz=G[u].size();q.pop();
for(int k=0;k<siz;k++){
int v=G[u][k];if(vis[v])continue;
dis[i][j][v]=dis[i][j][u];
ct[i][j][v]=ct[i][j][u];
sm[i][j][v]=sm[i][j][u];
if(L[i]<=ort[sta[v]]&&ort[sta[v]]<=R[i]){
ct[i][j][v]++;sm[i][j][v]+=b[val[sta[v]]];
dis[i][j][v]+=1ll*(T.queryCnt(root[u],0,totb,val[sta[v]],totb)+1)*b[val[sta[v]]];
dis[i][j][v]+=T.querySum(root[u],0,totb,0,val[sta[v]]-1);
T.insert(root[v],root[u],0,totb,val[sta[v]]);
}
else root[v]=root[u];
vis[v]=1;q.push(v);
}
}
T.clear();for(int k=1;k<=stak;k++)vis[k]=root[k]=0;
}
for(int j=1;j<=stak;j++)imp[sta[j]]=j;ip[i][1]=0;
while(!q.empty())q.pop();q.push(1);
while(!q.empty()){
int u=q.front();q.pop();if(imp[u])ip[i][u]=imp[u];
for(int j=head[u];j;j=e[j].nxt)
ip[i][e[j].to]=ip[i][u],q.push(e[j].to);
}
for(int j=1;j<=stak;j++)imp[sta[j]]=0,G[j].clear();
}
int cp=1;
for(int i=1;i<=m;i++){
char ch[5]={};scanf("\n%s",ch+1);
if(ch[1]=='C')read(cp);
else{
int u=cp,v;read(v);v=(v+(lastans&1)*key)%n+1;
if(dep[u]>dep[v])swap(u,v);int u_v=lca(u,v);
LL ans=0;int num=0;
for(int j=block[n];j>0;j--){
int au=ip[j][u],av=ip[j][v];
if(dfn[u_v]<=min(dfn[p[j][au]],dfn[p[j][av]])){
if(au&&av)ans+=dis[j][au][av]+1ll*num*sm[j][au][av],num+=ct[j][au][av];
else if(au&&!av)ans+=dis[j][1][av]+1ll*num*sm[j][1][av],num+=ct[j][au][av];
else if(av&&!au)ans+=dis[j][1][au]+1ll*num*sm[j][1][au],num+=ct[j][au][av];
}
else{
if(dfn[p[j][max(au,av)]]<dfn[u_v])continue;if(au>av)swap(au,av);au=av;
for(int k=8;k>=0;k--)if(dfn[p[j][ff[j][au][k]]]>=dfn[u_v])au=ff[j][au][k];
ans+=dis[j][au][av]+1ll*num*sm[j][au][av];num+=ct[j][au][av];
}
}
print(ans);putchar('\n');lastans=ans;
}
}
return 0;
}