bzoj 3460: Jc的宿舍 莫队算法

       一开始感觉是分块,但是好像不太兹瓷。于是觉得是莫队。

      但是他有强制在线,,而且莫队还是N^1.5logN的,感觉很不兹瓷。

      后来发现是假的在线。。。并且找到了一个题解发现就是N^1.5logN的,然后就做完了。

      yy了一个做法就是每16个分成一块,然后O(16)修改,O(N/16)询问,配合莫队就是O(16N^1.5+N^2/16),不知道能不能过(当然不是16应该也是兹瓷的)。

AC代码如下:

#include<bits/stdc++.h>
#define ll long long
#define N 200005
using namespace std;

int n,m,rt,pt,key,blk,dfsclk,cnt,fst[N],nxt[N],a[N],q[N],rk[N];
int d[N],fa[N],son[N],sz[N],anc[N],lf[N],rg[N],id[N];
ll res,c[N],ans[N]; bool vis[N];
struct node{ int x,y,k; }b[N];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
int lca(int x,int y){
	for (; anc[x]!=anc[y]; x=fa[anc[x]])
		if (d[anc[x]]<d[anc[y]]) swap(x,y);
	return (d[x]<d[y])?x:y;
}
void ins(int x,int y){
	if (lf[x]>lf[y]) swap(x,y);
	cnt++; b[cnt]=(node){(lca(x,y)==x)?lf[x]:rg[x],lf[y],cnt+1};
}
void add(int x,ll y){
	for (; x<=n; x+=x&-x) c[x]+=y;
}
ll getsum(int x){
	ll y=0; for (; x; x^=x&-x) y+=c[x]; return y;
}
void mdy(int y){
	int x=rk[y]; vis[y]^=1;
	if (vis[y]){
		pt++; add(x,(ll)a[y]*n+1);
		ll z=getsum(x)-1; res+=(ll)a[y]*(pt-z%n-1)+z/n;
	} else{
		ll z=getsum(x)-1; res-=a[y]*(pt-z%n-1)+z/n;
		pt--; add(x,-((ll)a[y]*n+1));
	}
}
void dfs(int x){
	int y; sz[x]=1;
	for (y=fst[x]; y; y=nxt[y]){
		d[y]=d[x]+1;
		dfs(y); sz[x]+=sz[y];
		if (sz[y]>sz[son[x]]) son[x]=y;
	}
}
void dvd(int x,int tp){
	int y; anc[x]=tp;
	lf[x]=++dfsclk; id[dfsclk]=x;
	if (son[x]) dvd(son[x],tp);
	for (y=fst[x]; y; y=nxt[y])
		if (y!=son[x]) dvd(y,y);
	rg[x]=++dfsclk; id[dfsclk]=x;
}
bool cmp1(int x,int y){ return a[x]<a[y]; }
bool cmp2(node u,node v){ return (u.x/blk==v.x/blk)?u.y<v.y:u.x<v.x; }
int main(){
	n=read(); m=read(); key=read();
	blk=(int)sqrt(2*n);
	int i,x,y,z; char ch[10];
	for (i=1; i<=n; i++){ a[i]=read(); q[i]=i; }
	sort(q+1,q+n+1,cmp1);
	for (i=1; i<=n; i++) rk[q[i]]=i;
	for (i=1; i<=n; i++){
		scanf("%d",&x);
		if (x){
			fa[i]=x; nxt[i]=fst[x]; fst[x]=i;
		}	else rt=i;
	}
	d[rt]=1; dfs(rt); dvd(rt,rt);
	for (i=x=1; i<=m; i++){
		scanf("%s",ch); y=read();
		if (ch[0]=='C') x=y; else{
			ins(x,y%n+1); ins(x,(y+key)%n+1);
		}
	}
	sort(b+1,b+cnt+1,cmp2);
	int l=1,r=0;
	for (i=1; i<=cnt; i++){
		while (b[i].x<l) mdy(id[--l]); while (b[i].y>r) mdy(id[++r]);
		while (b[i].x>l) mdy(id[l++]); while (b[i].y<r) mdy(id[r--]);
		x=id[l]; y=id[r]; z=lca(x,y);
		if (x!=z) mdy(z); ans[b[i].k]=res; if (x!=z) mdy(z);
	}
	ll tmp=0;
	for (i=1; i<=(cnt>>1); i++){
		tmp=ans[i<<1|(tmp&1)];
		printf("%lld\n",tmp);
	}
	return 0;
}


by lych

2017.2.19

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值