吼题啊
刚开始看上去又以为是LCT啥子的。
后来发现,TM是个图。
然后果断准备放弃,突然发现只有N个点N条边。
woc,这不就一个基环树上树链剖分吗。。。
关于基环树问题,相信大家都一定很有经验了吧,用个并查集找出多的边,然后把图分成一棵树和一条边,然后树上就树上做,多出来的边就可以另外处理。
关于边权转点权,直接在查询的最后减去LCA的点权就OK了,其他当成点权处理。
关于线段树,这个大家都会改吧。。。
关于修改操作,直接找出相应的点权,然后修改就行了,找点权记得先边的编号乘2
总之还是很简单的
下面可以看看代码注释:
#include#define maxn 4000001#define L(x) (x<<1)#define R(x) ((x<<1)|1)using namespace std;int tree[maxn],tag[maxn];int rev[maxn],dep[maxn],size[maxn],seg[maxn],top[maxn],son[maxn],father[maxn];int n,m,root,x,y,z,a[maxn],visx[maxn],visy[maxn],tot,mode;int cnt,from[maxn],to[maxn],Next[maxn],head[maxn];int Gx,Gy,Gz,Gd;int fa[maxn],X[maxn],Y[maxn],Z[maxn];int find(int x){ if(fa[x]==x)return x; else return fa[x]=find(fa[x]);}bool merge(int x,int y){ int i=find(x),j=find(y); if(i==j)return false; fa[i]=j;return true;}void add(int x,int y){ cnt++; from[cnt]=x;to[cnt]=y; Next[cnt]=head[x];head[x]=cnt;}void update(int node,int begin,int end,int x,int y,int val){ if(begin>y||end =x&&end<=y){ tree[node]=val; return; }else{ int mid=(begin+end)>>1; if(x<=mid)update(L(node),begin,mid,x,y,val); if(y>mid) update(R(node),mid+1,end,x,y,val); tree[node]=tree[L(node)]+tree[R(node)]; }}int query(int node,int begin,int end,int x,int y){ if(begin>=x&&end<=y){ return tree[node]; }else{ int mid=(begin+end)>>1,sum=0; if(x<=mid)sum+=query(L(node),begin,mid,x,y); if(y>mid) sum+=query(R(node),mid+1,end,x,y); return sum; }}int dfs1(int x){ size[x]=1; dep[x]=dep[father[x]]+1; for(int i=head[x];i!=-1;i=Next[i]){ int v=to[i],big=0; if(father[x]==v)continue; father[v]=x; big=dfs1(v); size[x]+=big; if(big>size[son[x]])son[x]=v; } return size[x]; }void dfs2(int x){ if(son[x]){ seg[son[x]]=++seg[0]; top[son[x]]=top[x]; rev[seg[0]]=son[x]; dfs2(son[x]); } for(int i=head[x];i!=-1;i=Next[i]){ int v=to[i]; if(!top[v]){ seg[v]=++seg[0]; top[v]=v; rev[seg[0]]=v; dfs2(v); } }}int linkquery(int x,int y){ int fx=top[x],fy=top[y],ans=0; while(fx!=fy){ if(dep[fx] dep[y])swap(x,y); ans+=query(1,1,seg[0],seg[x],seg[y]); ans-=query(1,1,seg[0],seg[x],seg[x]); return ans;}void change(int x,int y){ //修改边权 x*=2; //记得乘2 x=dep[from[x]]>dep[to[x]]?from[x]:to[x]; //因为边权钦定的是深度大的点 update(1,1,seg[0],seg[x],seg[x],y); //直接改}int main(){ memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m);root=1; for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=n;i++){ scanf("%d%d%d",&x,&y,&z); if(merge(x,y)){add(x,y),add(y,x);} else Gx=x,Gy=y,Gz=z,Gd=i,cnt+=2; //并查集判多的边 X[i]=x;Y[i]=y;Z[i]=z; } dfs1(root); seg[root]=++seg[0]; rev[seg[0]]=root; top[root]=root; dfs2(root); for(int i=1;i<=n;i++)if(Gd!=i)change(i,Z[i]); //因为本人懒得写build,所以这样写 for(int i=1;i<=m;i++){ scanf("%d%d%d",&mode,&x,&y); if(mode==1){ if(x==Gd)Gz=y; //如果是多余的边就直接改 else change(x,y); //不然就另外改 }else{ int ans; ans=linkquery(x,y); //第一种情况,不经过多余的边 ans=min(ans,Gz+min(linkquery(x,Gx)+linkquery(Gy,y),linkquery(x,Gy)+linkquery(Gx,y))); //第二种情况,经过多余的边 printf("%d\n",ans); } }}