点分治和点分树

吐槽

去年送我退役的好东西。写了一道题就觉得自己已经会动态点分治的我真是too young too simple,sometimes naive.
现在至少不像去年抓破脑壳写个模板了。然鹅这并不影响我小bug一堆。这里数组越界,那里没有清空,那里变量改变了。。

题目

[IOI2011]Race

把我心态搞崩的好模板。
就是一个再普通不过的点分治模板,然鹅我死都调不过。重构代码还是不过,最后怎么改怎么改我也不知道该到哪里就过了。大概有d[0]被赋为n和tot-sz[x]写成tot-sz[to[i]]。
李巨教我直接判断儿子和我的sz大小关系来得到儿子实际的sz。
どこでもドア

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=200007,M=1000007;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,ans,K,d[M];

template<typename T> void read(T &x) {
    char ch=getchar(); T f=1; x=0;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

int ecnt,fir[N],nxt[N<<1],to[N<<1],val[N<<1];
void add(int u,int v,int w) {
    nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
    nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w;
}

int RT,sz[N],msz[N],vis[N];
void get_root(int x,int fa,int tot) {
    sz[x]=msz[x]=1;
    for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]]) {
        get_root(to[i],x,tot);
        sz[x]+=sz[to[i]];
        msz[x]=max(msz[x],sz[to[i]]);
    }
    msz[x]=max(msz[x],tot-sz[x]);
    if(!RT||msz[x]<msz[RT]) RT=x;
}

int sta[N],R[N],H[N],top;
void dfs(int x,int fa,int now,int cc) {
    if(now>K) return;
    sta[++top]=x; 
    R[x]=cc; H[x]=now; 
    ans=min(ans,d[K-now]+cc);
    for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]]) 
        dfs(to[i],x,now+val[i],cc+1);
}

void solve(int x,int tot) {
    vis[x]=1; int pr=0;
    for(int i=fir[x];i;i=nxt[i]) if(!vis[to[i]]) {
        dfs(to[i],x,val[i],1);
        For(i,pr+1,top) d[H[sta[i]]]=min(d[H[sta[i]]],R[sta[i]]); pr=top;
    }
    while(top) d[H[sta[top--]]]=n; d[0]=0;
    for(int i=fir[x];i;i=nxt[i]) if(!vis[to[i]]) {
        RT=0; int lsz=sz[to[i]]>sz[x]?tot-sz[x]:sz[to[i]];
        get_root(to[i],x,lsz); solve(RT,lsz);
    }
}

int main() {
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    read(n); read(K);
    For(i,1,K) d[i]=n; ans=n;
    For(i,2,n) {
    	int u,v,w;
    	read(u); read(v); read(w);
    	add(u+1,v+1,w);
    }
    get_root(1,0,n); solve(1,n);
    if(ans==n) ans=-1;
    printf("%d\n",ans); 
    Formylove;
}

cf716 E. Digit Tree

どこでもドア
边上有1~9的数字,求路径上字符串连起来的数字mod p=0的路径个数。

路径掰两半, l v ∗ 1 0 d e p r + r v ≡ 0   ( m o d   p ) lv*10^{dep_r}+rv \equiv 0\ (mod\ p) lv10depr+rv0 (mod p)
典型点分。(p,10)=1叫你用exgcd求10的逆元。

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=100007;
typedef long long LL;
typedef double db;
using namespace std;
int n,p;
LL ans,pr[N],prinv[N];

template<typename T> void read(T &x) {
	char ch=getchar(); T f=1; x=0;
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') f=-1,ch=getchar();
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

int ecnt,fir[N],nxt[N<<1],to[N<<1],val[N<<1];
void add(int u,int v,int w) {
	nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
	nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w;
}

int RT,sz[N],msz[N],vis[N];
void get_root(int x,int fa,int tot) {
	sz[x]=msz[x]=1;
	for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]]) {
		get_root(to[i],x,tot);
		sz[x]+=sz[to[i]];
		msz[x]=max(msz[x],sz[to[i]]);
	}
	msz[x]=max(msz[x],tot-sz[x]);
	if(!RT||msz[x]<msz[RT]) RT=x;
}

int sta[N],top,Lv[N],Rv[N],dep[N];
void dfs(int x,int fa,int R,int lv,int rv) {
	sta[++top]=x;
	dep[x]=R; Lv[x]=lv; Rv[x]=rv;
	if(lv%p==0) ans++;
	if(rv%p==0) ans++;
	for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]])
		dfs(to[i],x,R+1,(pr[R]*val[i]+lv)%p,((LL)rv*10+val[i])%p);
}

void exgcd(LL a,LL b,LL &d,LL &x,LL &y) {
	if(!b) { d=a; x=1; y=0; return; }
	exgcd(b,a%b,d,y,x); y-=a/b*x;
}
LL get_inv(int a,int p) {
	LL d,x,y; exgcd(a,p,d,x,y);
	return (x%p+p)%p;
}

map<int,int>mp;
void calc(int l,int r,int f) {
	For(i,l,r) {
		int x=sta[i],v=((LL)p-Rv[x])%p*prinv[dep[x]]%p;
		ans+=f*mp[v]; mp[Lv[x]]++;
	}
	For(i,l,r) mp[Lv[sta[i]]]--;
	Rep(i,r,l) {
		int x=sta[i],v=((LL)p-Rv[x])%p*prinv[dep[x]]%p;
		ans+=f*mp[v]; mp[Lv[x]]++;
	}
	For(i,l,r) mp[Lv[sta[i]]]--; 
}

void solve(int x,int tot) {
	vis[x]=1;
	int szx=sz[x],prtop=0; 
	for(int i=fir[x];i;i=nxt[i]) if(!vis[to[i]]) {
		dfs(to[i],x,1,val[i],val[i]); 
		calc(prtop+1,top,-1); prtop=top;
	}
	calc(1,top,1); top=0;
	for(int i=fir[x];i;i=nxt[i]) if(!vis[to[i]]) {
		RT=0; int lsz=sz[to[i]]>szx?tot-szx:sz[to[i]];
		get_root(to[i],x,lsz); solve(RT,lsz);
	}
}

int main() {
    //freopen("1.in","r",stdin);
    //freopen("3730.out","w",stdout);
    read(n); read(p);
    pr[0]=1; prinv[0]=1;
	prinv[1]=get_inv(10,p);
	For(i,1,n) {
		pr[i]=pr[i-1]*10%p;
		if(i>1) prinv[i]=prinv[i-1]*prinv[1]%p;
	}
    For(i,2,n) {
    	int u,v,w;
    	read(u); read(v); read(w);
    	add(u+1,v+1,w%p);
	}
	get_root(1,0,n);
	solve(RT,n);
	printf("%I64d\n",ans);
    Formylove;
}

cf293 E. Close Vertices

どこでもドア
问路径长度小于等于l权值小于等于w的路径数。一维排序双指针一维树状数组即可。一个容斥的小模板。

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=100007;
typedef long long LL;
typedef double db;
using namespace std;
int n,L,W;
LL ans;

template<typename T> void read(T &x) {
	char ch=getchar(); T f=1; x=0;
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') f=-1,ch=getchar();
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

int ecnt,fir[N],nxt[N<<1],to[N<<1],val[N<<1];
void add(int u,int v,int w) {
	nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
	nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w;
}

int RT,sz[N],msz[N],vis[N];
void get_root(int x,int fa,int tot) {
	sz[x]=msz[x]=1;
	for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]]) {
		get_root(to[i],x,tot);
		sz[x]+=sz[to[i]];
		msz[x]=max(msz[x],sz[to[i]]);
	}
	msz[x]=max(msz[x],tot-sz[x]);
	if(!RT||msz[x]<msz[RT]) RT=x;
}

int sta[N],top,R[N],H[N];
void dfs(int x,int fa,int now,int cc) {
	if(now>W||cc>L) return;
	else ans++;
	R[x]=cc; 
	H[x]=now;
	sta[++top]=x;
	for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]])
		dfs(to[i],x,now+val[i],cc+1);
}

int cmp(const int &A,const int &B) { return H[A]<H[B]||(H[A]==H[B]&&R[A]<R[B]); }	

int sum[N];
void add(int x,int v) {
	for(int i=x;i<=L;i+=(i&(-i)))
		sum[i]+=v;
}

int qry(int x) {
	int rs=0;
	for(int i=x;i;i-=(i&(-i)))
		rs+=sum[i];
	return rs;
}

int a[N],le;
void calc(int l,int r,int f) {
	le=r-l+1; int np=0;
	For(i,l,r) a[i-l+1]=sta[i];
	sort(a+1,a+le+1,cmp);
	For(i,1,le) {
		while(np&&H[a[np]]+H[a[i]]>W) {
			add(R[a[np]],-1); np--;
		}
		ans+=f*qry(L-R[a[i]]);
		if(i<le&&H[a[i]]+H[a[i+1]]<=W) {
			np++; add(R[a[i]],1);
		}
	}
	For(i,1,np) add(R[a[i]],-1);
}

void solve(int x,int tot) {
	vis[x]=1;
	int szx=sz[x],prtop=0; 
	for(int i=fir[x];i;i=nxt[i]) if(!vis[to[i]]) {
		dfs(to[i],x,val[i],1); 
		calc(prtop+1,top,-1); prtop=top;
	}
	calc(1,top,1); top=0;
	for(int i=fir[x];i;i=nxt[i]) if(!vis[to[i]]) {
		RT=0; int lsz=sz[to[i]]>szx?tot-szx:sz[to[i]];
		get_root(to[i],x,lsz); solve(RT,lsz);
	}
}

int main() {
    //freopen("1.in","r",stdin);
    //freopen("3730.out","w",stdout);
    read(n); read(L); read(W);
    For(i,2,n) {
    	int u,v;
    	read(u); read(v);
    	add(i,u,v);
	}
	get_root(1,0,n);
	solve(RT,n);
	printf("%I64d\n",ans);
    Formylove;
}

bzoj3730: 震波

どこでもドア
以前写过但是当时写得非常痛苦的题。
指针动态分配内存搞树状数组也太好用了吧!但是查询的时候要和up取min不然会RE啊啊啊!
还有记下我点分树上的每一级祖先和我到他们的距离,以前的我还写个求lca来找距离是不是傻。。
每个点两个树状数组装子树内的点,一个按到自己的距离一个按到自己点分树父亲的距离排。

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=100007;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,val[N],tmp1[N*30],tmp2[N*30];

template<typename T> void read(T &x) {
	char ch=getchar(); T f=1; x=0;
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') f=-1,ch=getchar();
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

int ecnt,fir[N],nxt[N<<1],to[N<<1];
void add(int u,int v) {
	nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
	nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;
}

int RT,sz[N],msz[N],vis[N];
void get_root(int x,int fa,int tot) {
	sz[x]=msz[x]=1;
	for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]]) {
		get_root(to[i],x,tot);
		sz[x]+=sz[to[i]];
		msz[x]=max(msz[x],sz[to[i]]);
	}
	msz[x]=max(msz[x],tot-sz[x]);
	if(!RT||msz[x]<msz[RT]) RT=x;
}

int *sum[2][N],*np1=tmp1+1,*np2=tmp2+1;
void ADD(int o,int id,int x,int v) {
	for(int i=x;i<=sz[id];i+=(i&(-i)))
		sum[o][id][i]+=v;
}

int qry(int o,int id,int x) {
	int rs=0;
	for(int i=min(sz[id],x);i;i-=(i&(-i)))
		rs+=sum[o][id][i];
	return rs;
}

vector<int>ds[N];
void dfs(int x,int fa,int R) {
	ds[x].push_back(R); 
	for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]]) 
		dfs(to[i],x,R+1);
}

int p[N][20],dis[N][20];
void build(int x,int tot) {
	vis[x]=1;
	int szx=sz[x]; 
	sz[x]=tot;
	p[x][0]=x;
	dfs(x,0,0); 
	sum[0][x]=np1; sum[1][x]=np2; 
	np1+=sz[x]+1; np2+=sz[x]+1;
	int up=ds[x].size()-1;
	For(i,0,18) {
		if(i>1) p[x][i]=p[p[x][1]][i-1];
		if(p[x][i]) dis[x][i]=ds[x][up--];
	}
	For(i,0,18) if(p[x][i]) {
		ADD(0,p[x][i],dis[x][i]+1,val[x]);
		if(p[x][i+1]) ADD(1,p[x][i],dis[x][i+1],val[x]);
	}
	for(int i=fir[x];i;i=nxt[i]) if(!vis[to[i]]) {
		RT=0; int lsz=sz[to[i]]>szx?tot-szx:sz[to[i]];
		get_root(to[i],x,lsz); p[RT][1]=x; build(RT,lsz);
	}
}

void change(int x,int y) {
	For(i,0,18) if(p[x][i]) {
		ADD(0,p[x][i],dis[x][i]+1,y-val[x]);
		if(p[x][i+1]) ADD(1,p[x][i],dis[x][i+1],y-val[x]);
	}
	val[x]=y;
}

int lastans;
void Qry(int x,int k) {
	int rs=0;
	For(i,0,18) {
		if(!p[x][i]) break;
		if(k>=dis[x][i]) rs+=qry(0,p[x][i],k-dis[x][i]+1);
		if(p[x][i+1]&&k>dis[x][i+1]) rs-=qry(1,p[x][i],k-dis[x][i+1]);
	}
	printf("%d\n",rs); lastans=rs;
}

int main() {
    //freopen("3730.in","r",stdin);
    //freopen("3730.out","w",stdout);
    read(n); read(m);
    For(i,1,n) read(val[i]);
    For(i,2,n) {
    	int u,v;
    	read(u); read(v);
    	add(u,v);
	}
	get_root(1,0,n);
	build(1,n);
	For(i,1,m) {
		int op,x,y;
		read(op); read(x); read(y);
		x^=lastans; y^=lastans;
		if(!op) Qry(x,y);
		else change(x,y);
	}
    Formylove;
}

cf757 G. Can Bash Save the Day?

どこでもドア
题意:给你一棵n个点的树和一个排列 p i {p_i} pi,边有边权。有q个操作:

1 l r x:询问 ∑ i = l r d i s t ( p i , x ) \sum\limits_{i=l}^r dist(p_i,x) i=lrdist(pi,x)
2 x: s w a p ( p x , p x + 1 ) swap(p_x,p_{x+1}) swap(px,px+1)

n , q ≤ 2 × 1 0 5 n,q\le 2\times 10^5 n,q2×105,强制在线。

讲课人说这是可持久化点分树,我说这不是可以set水过嘛??然后开心地打了并且开心地T了。
每个点用set维护它管辖范围的点按在a中顺序的前缀和,就是两个log,然后70多个点的时候TLE了。

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=2e5+7;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,a[N],p[N];

template<typename T> void read(T &x) {
    char ch=getchar(); T f=1; x=0;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

struct node {
    int pos; LL sum[3];
    friend bool operator <(const node&A,const node&B) {
        return A.pos<B.pos;
    }
};
set<node>rt[N];

int ecnt,fir[N],nxt[N<<1],to[N<<1],val[N<<1];
void add(int u,int v,int w) {
    nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
    nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w;
}

int RT,sz[N],msz[N],vis[N];
void get_root(int x,int fa,int tot) {
    sz[x]=msz[x]=1;
    for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]]) {
        get_root(to[i],x,tot);
        sz[x]+=sz[to[i]];
        msz[x]=max(msz[x],sz[to[i]]);
    }
    msz[x]=max(msz[x],tot-sz[x]);
    if(!RT||msz[RT]>msz[x]) RT=x;
}

vector<LL>ds[N];
int sta[N],top;
void dfs(int x,int fa,LL R) {
    sta[++top]=x;
    ds[x].push_back(R);
    for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]]) 
        dfs(to[i],x,R+val[i]);
}

bool cmp(const int&A,const int&B) { return p[A]<p[B]; }

int f[N][20];
LL dis[N][20];
void build(int x,int tot) {
    vis[x]=1;
    top=0;
    dfs(x,0,0);
    sort(sta+1,sta+top+1,cmp);
    LL s1=0,s2=0,s3=0;
    For(i,1,top) {
        int u=sta[i],up=ds[u].size()-1;
        s1++; s2+=ds[u][up]; s3+=(up>0?ds[u][up-1]:0);
        rt[x].insert((node){p[u],s1,s2,s3});
    }
    f[x][0]=x; int up=ds[x].size()-1;
    For(i,0,18) {
        if(i>1) f[x][i]=f[f[x][1]][i-1];
        if(f[x][i]) dis[x][i]=ds[x][up--];
    }
    for(int i=fir[x];i;i=nxt[i]) if(!vis[to[i]]) {
        RT=0; int lsz=sz[to[i]]>sz[x]?tot-sz[x]:sz[to[i]];
        assert(lsz*2<=tot+5);
        get_root(to[i],x,lsz);
        f[RT][1]=x; build(RT,lsz);
    }
}

#define IT set<node>::iterator 
LL lastans;
void qry(int ql,int qr,int x) {
    LL rs=0;
    For(i,0,18) if(f[x][i]) {
        int y=f[x][i];
        IT it=rt[y].lower_bound((node){ql,0}); 
        if(it==rt[y].end()) continue;
        if(it!=rt[y].begin()) {
            --it; rs-=((*it).sum[1]+(*it).sum[0]*dis[x][i]);
            if(f[x][i+1]) rs+=((*it).sum[2]+(*it).sum[0]*dis[x][i+1]);
        }
        it=rt[y].lower_bound((node){qr,0});
        node tpp=*it;
        if(it==rt[y].end()||(*it).pos>qr) {
            if(it==rt[y].begin()) continue;
            --it;
        }
        tpp=*it;
        rs+=((*it).sum[1]+(*it).sum[0]*dis[x][i]);
        if(f[x][i+1]) rs-=((*it).sum[2]+(*it).sum[0]*dis[x][i+1]);
    }
    printf("%lld\n",rs); lastans=rs;
}

void change(int x) {
    For(i,0,18) if(f[a[x]][i]) {
        int u=f[a[x]][i];
        IT it=rt[u].lower_bound((node){x});
        node tp=*it; 
        rt[u].erase(it); 
        it=rt[u].lower_bound((node){x});
        if(it!=rt[u].end()&&(*it).pos==x+1) {
            node tp2=*it; 
            LL t1=tp.sum[1]-dis[a[x]][i]+tp2.sum[1]-tp.sum[1];
            LL t2=tp.sum[2]-dis[a[x]][i+1]+tp2.sum[2]-tp.sum[2];
            rt[u].insert((node){x,tp.sum[0],t1,t2});
        }
        else { tp.pos++; rt[u].insert(tp); } 
    }
    For(i,0,18) if(f[a[x+1]][i]) {
        int u=f[a[x+1]][i];
        IT it=rt[u].lower_bound((node){x+1,0}),it2=it;
        if(it2!=rt[u].begin()) it2--;
        if((*it2).pos==x) break;
        node t=*it; t.pos--;
        rt[u].erase(it); rt[u].insert(t);
    }
    swap(a[x],a[x+1]);
    p[a[x]]=x; p[a[x+1]]=x+1;
}

int main() {
    //freopen("1.in","r",stdin);
    //freopen("WA.out","w",stdout);
    read(n); read(m);
    For(i,1,n) read(a[i]),p[a[i]]=i;
    For(i,2,n) {
        int u,v,w;
        read(u); read(v); read(w);
        add(u,v,w);
    }
    RT=0; get_root(1,0,n);
    build(RT,n);
    For(i,1,m) {
        int op,x,l,r;
        read(op);
        if(op==1) {
            read(l); read(r); read(x);
            l^=lastans%(1<<30); r^=lastans%(1<<30); x^=lastans%(1<<30);
            qry(l,r,x);
        }
        else {
            read(x); 
            x^=lastans%(1<<30);
            change(x);
        }
    }
    Formylove;
}

没有办法只有可持久化点分树了。。。
按a中的顺序在上一次的基础上建树,每个点维护现在有贡献的点数目和到它和它父亲的距离和,每次会新建一条链。发现修改只会影响x这一棵树,前后的树都不变,所以直接重新在x-1的基础上建新的x就好了。
然后因为要可持久化,强行多叉转二叉(卡空间3个儿子只能多建一个新管辖点。。)。强行把自底向上的优秀点分树变成自顶向下的丑陋玩意。

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(register int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(register int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=2e5+7;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,a[N];

template<typename T> void read(T &x) {
    char ch=getchar(); T f=1; x=0;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

int ecnt,fir[N<<1],nxt[N<<2],to[N<<2],val[N<<2];
void add(int u,int v,int w) {
    nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
    nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w;
}

int top,cnt,bvs[N<<1];
#define pr pair<int,int>
#define MP make_pair
#define se second
#define fi first
vector<pr>son[N],son2[N<<1];
void prebuild(int x,int fa) {
    int up=son[x].size();
    For(i,0,up-1) if(son[x][i].fi!=fa) {
        son2[x].push_back(son[x][i]);
        prebuild(son[x][i].fi,x);
    }
}

void rebuild(int x) {
    int up=son2[x].size();
    bvs[x]=1;
    if(up<=2) {
        For(i,0,up-1) add(x,son2[x][i].fi,son2[x][i].se);
    }	
    else if(up==3) {
        son2[++cnt].push_back(son2[x][0]);
        son2[cnt].push_back(son2[x][1]);
        add(x,cnt,0); add(x,son2[x][2].fi,son2[x][2].se);
    }
    else {
        For(i,0,up-1) {
            if(i&1) son2[cnt+1].push_back(son2[x][i]);
            else son2[cnt+2].push_back(son2[x][i]);
        }
        add(x,++cnt,0); add(x,++cnt,0);
    }
    for(int i=fir[x];i;i=nxt[i]) if(!bvs[to[i]])
        rebuild(to[i]);
}

int RT,sz[N<<1],msz[N<<1],vis[N<<1];
void get_root(int x,int fa,int tot) {
    sz[x]=msz[x]=1;
    for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]]) {
        get_root(to[i],x,tot);
        sz[x]+=sz[to[i]];
        msz[x]=max(msz[x],sz[to[i]]);
    }
    msz[x]=max(msz[x],tot-sz[x]);
    if(!RT||msz[RT]>msz[x]) RT=x;
}

vector<LL>ds[N<<1];
void dfs(int x,int fa,LL R) {
    ds[x].push_back(R);
    for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa&&!vis[to[i]]) 
        dfs(to[i],x,R+val[i]);
}

int totnd,rt[N],tr[N*60],ch[N*60][3];
LL sum[N*60][3];

LL dis[N<<1][20];
int dfk,dfn[N<<1],low[N<<1],tR[N<<1];
int in(int a,int b) { return dfn[a]>=dfn[b]&&dfn[a]<=low[b]; }

void build(int x,int tot) {
    tr[x]=x;
    vis[x]=1;
    dfs(x,0,0);
    dfn[x]=low[x]=++dfk;
    int up=ds[x].size()-1,xson=0;
    For(i,0,18) if(up>=0) 
        dis[x][i]=ds[x][up--];    
    for(int i=fir[x];i;i=nxt[i]) if(!vis[to[i]]) {
        int lsz=sz[to[i]]>sz[x]?tot-sz[x]:sz[to[i]];
        RT=0; get_root(to[i],x,lsz); 
        ch[x][xson++]=RT; tR[RT]=tR[x]+1; 
        build(RT,lsz); low[x]=max(low[x],low[RT]);
    }
}

void upd(int &x,int last,int R,int nd) {
    x=++totnd;
    tr[x]=tr[last];
    For(i,0,2) ch[x][i]=ch[last][i],sum[x][i]=sum[last][i];
    sum[x][0]++;
    sum[x][1]+=dis[nd][tR[nd]-R];
    if(R) sum[x][2]+=dis[nd][tR[nd]-R+1];
    if(tr[x]==nd) return;
    For(i,0,2) if(ch[x][i]&&in(nd,tr[ch[x][i]]))
        upd(ch[x][i],ch[last][i],R+1,nd);
}

LL Qrs;
void preQ() { Qrs=0; }
void qry(int x,int R,int nd) {
    Qrs+=(sum[x][1]+sum[x][0]*dis[nd][tR[nd]-R]);
    if(R) Qrs-=(sum[x][2]+sum[x][0]*dis[nd][tR[nd]-R+1]);
    if(tr[x]==nd) return;
    For(i,0,2) if(ch[x][i]&&in(nd,tr[ch[x][i]]))
        qry(ch[x][i],R+1,nd);
}

LL lastans;
void Qry(int ql,int qr,int x) {
    LL rs=0;
  	preQ(); qry(rt[qr],0,x); rs+=Qrs;
  	preQ(); if(ql-1) qry(rt[ql-1],0,x); rs-=Qrs;
    printf("%I64d\n",rs); lastans=rs%(1<<30);
}

int main() {
    //freopen("std.in","r",stdin);
    //freopen("WA.out","w",stdout);
    read(n); read(m);
    For(i,1,n) read(a[i]);
    For(i,2,n) {
        int u,v,w;
        read(u); read(v); read(w);
        son[u].push_back(MP(v,w));
        son[v].push_back(MP(u,w));
    }
    cnt=n; 
    prebuild(1,0); rebuild(1);
    RT=0; get_root(1,0,cnt);
    rt[0]=RT; build(RT,cnt);
    totnd=cnt;
    For(i,1,n) 
        upd(rt[i],rt[i-1],0,a[i]);
    int qc=0;
    For(i,1,m) {
        int op,x,l,r;
        read(op);
        if(op==1) {
            read(l); read(r); read(x);
            l^=lastans; r^=lastans; x^=lastans;
            Qry(l,r,x);
        }
        else {
            read(x); x^=lastans;
          	upd(rt[x],rt[x-1],0,a[x+1]);
            swap(a[x],a[x+1]);
        }
    }
    Formylove;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值