[BZOJ3460]Jc的宿舍

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(nm logn)
事实上有点卡常。

源码

#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;
}

谢谢!!!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值