树剖_2。

迫不得已啊,有一个很有意思的换根题,我决定看一看Jamie and Tree,就是这一个啦,具体的我放代码里,这编辑的太卡了,写的东西一多就这样:受不了愚蠢树剖但我是sb在这里插入图片描述
写完就裂开了调了好久呜呜呜~

//看到换根那直接想到分类讨论;设1为主根,rt为当前根,u是查询的点 
//若rt==u 那毕竟是查最小值相对会简单一点(异或等可能会难) retunr sum[1] 即可。
//若u不是rt的祖先,即u不在rt到1的路上。那其实rt在哪里都不是问题,直接正常查询就行,画图想想就行 
//若 是 那么就要加上1到u这一段的和了,具体的话因为是dfsx,那么处理起来就可以直接用1的sum-不要的子树的sum 
//但! 这是简单的 ,若是区间极值可就不能这样了罢,我们可以选择分两段查 
//但! 这只是查询,修改恶心的要死,不说~只说lca的求法:LCA 就是 lca(x,y),lca(x,rt	),lca(y,rt) 这三者中深度 最大 
#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len=0,root,last[200001],ybh[200001],a[200001],db[100001][30];
struct pp
{
	int x,y,next;
};pp p[400001];
struct TR
{
	int dfsx,son,fa,dd,dep,siz;
};TR tr[200001];
struct node 
{
	int l,r,lc,rc,sum,lazy;
};node e[4000001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
int bt(int x,int y)
{
	int now=++len;
	int l=x,r=y,lc=-1,rc=-1,sum;
	if(l==r) sum=a[ybh[x]];
	else 
	{
		int mid=(l+r)/2;
		lc=bt(l,mid);rc=bt(mid+1,r);
		sum=e[lc].sum+e[rc].sum;
	}	
	e[now]={l,r,lc,rc,sum,0};
	return now;
}
void dfs1(int now,int fa)
{
	tr[now].dep=tr[fa].dep+1;tr[now].fa=fa;
	tr[now].siz=1;tr[now].son=-1;db[now][0]=fa;
	for(int i=1;(1<<i)<=tr[now].dep;i++) db[now][i]=db[db[now][i-1]][i-1];
	for(int i=last[now];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;
		dfs1(y,now);tr[now].siz+=tr[y].siz;
		if(tr[now].siz==-1||tr[tr[now].son].siz<tr[y].siz) tr[now].son=y; 
	}
	return ;
}
void dfs2(int now,int dd)
{
	tr[now].dd=dd;tr[now].dfsx=++len;ybh[len]=now;
	if(tr[now].son==-1) return ; dfs2(tr[now].son,dd);
	for(int i=last[now];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==tr[now].son||y==tr[now].fa) continue ;
		dfs2(y,y);
	}
	return ;
} 
void pushdown(int now)
{
	if(e[now].lazy==0) return ;
	int k=e[now].lazy,lc=e[now].lc,rc=e[now].rc;e[now].lazy=0;
	e[lc].lazy+=k;e[lc].sum+=k*(e[lc].r-e[lc].l+1);
	e[rc].lazy+=k;e[rc].sum+=k*(e[rc].r-e[rc].l+1);
	return ;
}
void add(int now,int x,int y,int k)
{
	int l=e[now].l,r=e[now].r;
	if(x==l&&y==r) e[now].lazy+=k,e[now].sum+=k*(r-l+1);
	else 
	{
		int lc=e[now].lc,rc=e[now].rc,mid=(l+r)/2;pushdown(now);
		if(x>=mid+1) add(rc,x,y,k);
		else if(y<=mid) add(lc,x,y,k);
		else add(lc,x,mid,k),add(rc,mid+1,y,k);
		e[now].sum=e[lc].sum+e[rc].sum;
	} 
	return ;
}
int findsum(int now,int x,int y)
{
	int l=e[now].l,r=e[now].r;
	if(l==x&&y==r) return e[now].sum;
	else 
	{
		int lc=e[now].lc,rc=e[now].rc,mid=(l+r)/2;pushdown(now);
		if(x>=mid+1) return findsum(rc,x,y);
		else if(y<=mid) return findsum(lc,x,y);
		else return findsum(lc,x,mid)+findsum(rc,mid+1,y);
	}
}
int getlca(int x,int y)
{
	while(tr[x].dd!=tr[y].dd)
	{
		if(tr[tr[x].dd].dep<tr[tr[y].dd].dep) swap(x,y);
		x=tr[tr[x].dd].fa;
	}
	if(tr[x].dep>tr[y].dep) swap(x,y); 
	return x;
}
int jump(int x,int depth)
{
	if(depth<=0) return x;
	int now=x;
	for(int i=20;i>=0;i--)
	{
		if(depth&(1<<i)) now=db[now][i];
	}
	return now;
}

signed main()
{
	memset(last,-1,sizeof(last));
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1;i<=n-1;i++)
	{
		int x,y;scanf("%lld%lld",&x,&y);
		ins(x,y);ins(y,x);
	}
	int ST=1;dfs1(ST,ST);len=0;dfs2(ST,ST);
	len=0;root=bt(1,n);
	while(m--)
	{
		int op;scanf("%lld",&op);
		if(op==1) scanf("%lld",&ST);
		else if(op==2)
		{
			int x,y,lca,k;scanf("%lld%lld%lld",&x,&y,&k);
			int q1=getlca(x,y),q2=getlca(x,ST),q3=getlca(y,ST);
			if(tr[q1].dep<tr[q2].dep) q1=q2;
    		if(tr[q1].dep<tr[q3].dep) q1=q3;
			x=q1;int l=tr[x].dfsx,r=tr[x].dfsx+tr[x].siz-1;
    		if(x==ST) add(root,tr[1].dfsx,tr[1].dfsx+tr[1].siz-1,k);
		    else if(tr[ST].dfsx<l||tr[ST].dfsx>r) add(root,l,r,k);
		    else 
			{
		        int son=jump(ST,tr[ST].dep-tr[x].dep-1);
				add(root,tr[1].dfsx,tr[1].dfsx+tr[1].siz-1,k);
				add(root,tr[son].dfsx,tr[son].dfsx+tr[son].siz-1,-k);
		    }
		}
		else 
		{
			int x;scanf("%lld",&x);
			if(x==ST) printf("%lld\n",findsum(root,tr[1].dfsx,tr[1].dfsx+tr[1].siz-1));
			else 
			{
				int lca=getlca(x,ST);
				if(lca==x) 
				{
					int noew=jump(ST,tr[ST].dep-tr[x].dep-1);
					printf("%lld\n",findsum(root,tr[1].dfsx,tr[1].dfsx+tr[1].siz-1)-findsum(root,tr[noew].dfsx,tr[noew].dfsx+tr[noew].siz-1));
				}
				else printf("%lld\n",findsum(root,tr[x].dfsx,tr[x].dfsx+tr[x].siz-1));
			}
		}
	}
	return 0;
} 

好的换了一个新的板块,来一道Minimum spanning tree for each edge,一道难以看出是树剖的题目,不过看出来之后就和之前那道呃上一篇有一题删边转连边的那个有异曲同工之妙,具体而言,毕竟是最小生成树,那得是树咯。树,考虑加上一条在树上的边,那肯定不会有什么变化的,那要是不在树上的话,考虑从图形入手,那肯定会形成一个环,那么我们就要删去一条边(加入的除外),一条边权最大的边。但是!只能减去环上的最大值,至于为什么,因为它要把环废掉罢。那么就搞定了!然后的话有些小细节什么不要在意啦。有个问题!是边权转点权,懂得都懂。不想玩了,我三十分钟打代码调了一个小时。!!!!!啊啊啊啊

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len=0,root,ans=0,last[410001],fa[410001];
int a[410001],ybh[410001];
struct pp
{
	int x,y,c,next;
};pp p[820001];
struct node1
{
	int x,y,c,num;
};node1 ll[820001];
struct TR
{
	int dfsx,fa,son,dd,siz,dep;
};TR tr[820001];
struct node 
{
	int l,r,lc,rc,maxx;
};node e[820001];
bool cmp(const node1 &x,const node1 &y)
{
	return x.c<y.c;
}
bool cmp2(const node1 &x,const node1 &y)
{
	return x.num<y.num;
}
int findfa(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=findfa(fa[x]);
}
void ins(int x,int y,int c)
{
	int now=++len;
	p[now]={x,y,c,last[x]};last[x]=now;
	return ;
}
int bt(int x,int y)
{
	int now=++len;
	int l=x,r=y,lc=-1,rc=-1,maxx;
	if(l==r) maxx=a[ybh[x]];
	else 
	{
		int mid=(l+r)/2;
		lc=bt(l,mid);rc=bt(mid+1,r);
		maxx=max(e[lc].maxx,e[rc].maxx);
	}
	e[now]={l,r,lc,rc,maxx};
	return now;
}
void dfs1(int now,int fa)
{
	tr[now].dep=tr[fa].dep+1;tr[now].fa=fa;
	tr[now].son=-1,tr[now].siz=1;
	for(int i=last[now];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==fa) continue ;
		dfs1(y,now);tr[now].siz+=tr[y].siz;a[y]=p[i].c;
		if(tr[now].son==-1||tr[tr[now].son].siz<tr[y].siz) tr[now].son=y;
	}	
	return ;
}
void dfs2(int now,int dd)
{
	tr[now].dd=dd;tr[now].dfsx=++len;ybh[len]=now;
	if(tr[now].son==-1) return ; dfs2(tr[now].son,dd);
	for(int i=last[now];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(y==tr[now].son||y==tr[now].fa) continue ;
		dfs2(y,y);
	}
	return ;
}
int findmax(int now,int x,int y)
{
	if(x>y) return 0;
	int l=e[now].l,r=e[now].r;
	if(l==x&&y==r) return e[now].maxx;
	else 
	{
		int lc=e[now].lc,rc=e[now].rc,mid=(l+r)/2;
		if(x>=mid+1) return findmax(rc,x,y);
		else if(y<=mid) return findmax(lc,x,y);
		else return max(findmax(lc,x,mid),findmax(rc,mid+1,y));
	}	
}
int askmax(int x,int y)//查询x到y路径上的最大值 
{
	int rt=0;
	while(tr[x].dd!=tr[y].dd)
	{
		if(tr[tr[x].dd].dep<tr[tr[y].dd].dep) swap(x,y);
		rt=max(rt,findmax(root,tr[tr[x].dd].dfsx,tr[x].dfsx));// ? ? ?
		x=tr[tr[x].dd].fa;
	}
	if(tr[x].dfsx>tr[y].dfsx) swap(x,y);
	rt=max(rt,findmax(root,tr[x].dfsx+1,tr[y].dfsx));
	return rt;
}
signed main()
{
	memset(last,-1,sizeof(last));
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++) fa[i]=i,a[i]=0;
	for(int i=1;i<=m;i++)
	{
		int x,y,c;scanf("%lld%lld%lld",&ll[i].x,&ll[i].y,&ll[i].c);
		ll[i].num=i;
	}
	sort(ll+1,ll+m+1,cmp);
	for(int i=1;i<=m;i++) 
	{
		int x=ll[i].x,y=ll[i].y,fx=findfa(x),fy=findfa(y);
		if(fx!=fy) 
		{
			fa[fx]=fy;ans+=ll[i].c;
			ins(x,y,ll[i].c);ins(y,x,ll[i].c);
		}
	}
	int ST=1;dfs1(ST,ST);len=0;dfs2(ST,ST);
	len=0;root=bt(1,n);sort(ll+1,ll+m+1,cmp2);
	for(int i=1;i<=m;i++)
	{
		int x=ll[i].x,y=ll[i].y;
		printf("%lld\n",ans+ll[i].c-askmax(x,y));
	}
	return 0;
}
//7 14
//2 4 25
//6 4 5
//5 6 3
//5 7 9
//6 1 17
//4 7 6
//5 4 25
//1 2 23
//2 3 15
//5 1 10
//7 6 21
//3 7 5
//5 3 4
//5 2 15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值