NOI 2022 Singapore Final 题解
如果你是因为标题中的“NOI 2022”字样点进来的,那……这套题确实叫 NOI 2022 嘛。毕竟新加坡的 OI 赛事也叫 NOI。
如果你想要提交这套题,那么可以 在 QOJ 上面找到题目。感谢 Qingyu
E 我还不会,所以这道题没有写题解。官方题解有点长,我作为洋文弱子读起来有点困难。有没有好哥哥能教教我啊
题面
注:这几道题的空间限制我不知道,暂时都按照 QOJ 上开的 1G 来吧。共 5 题
A
给定一张 \(n\) 个点 \(m\) 条边的带权有向图(点从 \(0\) 到 \((n-1)\) 编号),有 \(k\) 个点是关键点。你有 \(5\) 张门票,门票价格未知。第 \(i\) 种门票可以使一条路径的边权减少 \(10i\%\),一条路径上最多使用一种门票,每种门票最多购买一张。有 \(Q\) 次询问,每次给定一个起点和 \(5\) 种门票的价格(价格为 \(-1\) 代表不允许购买),求购买门票、前往一个关键点的最小代价和。
输入格式:第一行三个整数 \(n,m,k\),第二行 \(k\) 个整数描述关键点,接下来 \(m\) 行每行三个整数 \(u,v,w\),描述一条 \(u\rightarrow v\) 的有向边。接下来一行一个整数 \(Q\),最后 \(Q\) 行每行 \(6\) 个整数,描述每组询问的起点与门票价格。
输出格式:\(Q\) 行每行一个整数表示对应询问的答案,若无法到达任何一个关键点则输出 \(-1\)。
数据范围:\(n\le 5000,m\le 10000,Q\le100\),权值均在 \(10^9\) 以内,保证边权为 \(10\) 的倍数。
时间限制:1s
B
你有一个二元组 \((p,c)\),初始时其为 \((0,0)\)。有 \(n\) 个运算节点,每个节点上有一个二元组 \((x,L)\),执行的操作为:若 \(p\le L\),则令二元组变为 \((p+x,c+1)\)。需要合理安排二元组通过 \(n\) 个节点的顺序,使得最终的 \(c\) 尽量大。
输入格式:第一行一个整数 \(n\),第二行 \(n\) 个整数描述 \(x\),第三行 \(n\) 个整数描述 \(L\)。
输出格式:一行一个整数,表示最终最大的 \(c\)。
数据范围:\(n\le 5\times10^5,1\le x,L\le10^9\)
时间限制:1.5s
C
二维平面上有 \(n\) 个点,它们的位置互不相同。你要在其中一些点上打标记,使得最终局面满足以下条件:
- 任意水平或竖直直线上最多有两个点被标记。
- 对于 \(n\) 个点中的每一个,要么其自身被标记,要么其位于两个被标记的点组成的水平或竖直线段上。
输出方案。
输入格式:第一行一个整数 \(n\),下面 \(n\) 行每行两个数 \(x,y\),描述一个点。
输出格式:输出一个长度为 \(n\) 的 \(01\) 串,第 \(i\) 个位置为 \(0\) 表示不打标记,为 \(1\) 表示打标记。
数据范围:\(1\le n,x,y\le 10^6\)
时间限制:2s
D
有一棵树,边带权,节点有黑色 / 白色两种状态,初始所有点都是黑色。有三种操作:
- 询问某个点到白色点的最短距离
- 反转某个点的颜色
- 修改一条边的边权
对每次询问输出答案。
输入格式:第一行一个整数 \(n,m\),表示树的点数和询问数。下面 \((n-1)\) 行每行三个整数 \(u,v,w\),描述树的结构。接下来 \(m\) 行每行开头是一个整数 \(opt\),为 \(1\) 代表询问,后面一个整数 \(x\) 表示起点;为 \(2\) 代表反转,后面一个整数 \(x\) 表示要反转的点;为 \(3\) 代表修改边权,后面三个整数 \(a,b,w\) 代表将 \((a,b)\) 这条边的边权修改为 \(w\),保证这条边存在。
输出格式:对于每种 \(1\) 操作输出一行一个整数表示答案。如果当前树上没有白色点则输出 \(-1\)。
数据范围:\(n,m\le 10^5,0\le w\le 10^9\)
时间限制:3s
E
有 \(n\) 个物品和 \(n\) 个柜子,物品有价值。你要给柜子分配互不相同的物品,其中有些柜子的物品是钦定好的。
现在有一个人要经过编号为 \(1\) 到 \(k\) 的柜子,如果当前柜子中的物品价值严格大于他背包中任何一个物品的价值,或者他的背包是空的,那么他会将当前柜子中的物品放入背包中。
你需要最大化这个人获得的价值,对 \(1\) 到 \(n\) 的每个 \(k\) 求出答案。
输入格式:第一行一个整数 \(n\),第二行 \(n\) 个整数描述每个柜子被钦定的物品(\(-1\) 代表对应位置没有钦定),第三行 \(n\) 个整数按照不降顺序描述物品价值。
输出格式:一行 \(n\) 个整数,第 \(i\) 个整数代表 \(k=i\) 时的最大价值。
数据范围:\(n\le4\times10^5,w\le10^9\)
时间限制:1s
题解
A
建反图跑多个起点的分层图最短路,把买的门票集合记在状态里。
处理询问时枚举买了哪些门票即可。
B
- 性质 1:存在至少一种最优方案,使得若 \(i,j\) 两个位置均执行运算且 \(i\) 被扫在 \(j\) 前,则 \(x_i+L_i\le x_j+L_j\)。
- 证明:设 \(x_i+L_i>x_j+L_j\),扫 \(i\) 之前 \(p=S\),则有 \(S+x_i\le L_j\),即 \(S+x_i+x_j\le L_j+x_j<x_i+L_i\),化简后得到 \(S+x_j<L_i\),那么我们交换 \(i,j\) 后仍能得到一组合法方案。
- 性质 2:对于当前的所有运算节点,存在至少一种最优方案,满足在某一时刻对 \(x\) 最小的运算节点执行了运算。
- 证明:如果没有,那把第一个扫到的运算节点和 \(x\) 最小的交换,答案显然不会变差。
由此得到的一个思路:用线段树(以 \((x+L)\) 作为下标)维护所有运算节点。每次找到 \(x\) 最小的(设为 \(i\)),移除这个点,然后将线段树上排在它后面的运算节点 \(L\) 都减去 \(x_i\)。如果这之后有些节点 \(L<0\) 了,那么暴力在线段树上找到这些点并删掉(这部分显然均摊 \(1\log\) 所以后面不再说了)。
这样连样例 1 都过不去。问题在于我们每个时刻选出的点都只考虑了对于当前序列的最优方案,可能会违背之前的最优方案(把一个 \(x\) 比当前运算节点更小、已经被移除掉的节点 \(L\) 减到 \(<0\) 然后又删了一遍)。
于是再在线段树上维护一下“区间内已被移除的点的 \(L\) 最小值”,如果某次操作违背了之前的最优方案,那么就不能更新答案。
总时间复杂度 \(O(n\log n)\)
C
首先对于所有 \(x\) 相同的点,我们给 \(y\) 最小、最大的两个打上标记。
这样可能会导致某些水平直线上有多于两个点被选择了,我们依次处理其中每一条水平直线。
具体地,保留当前直线上 \(x\) 最大、最小的两个。对于中间的每个标记过的点,如果它是对应 \(x\) 被选的唯一一个点,那么直接删掉;否则如果是对应 \(x\) 选的两个点中 \(y\) 较小的一个,我们改选有相同 \(x\) 的点当中它的后继。同理如果是 \(y\) 较大的就改为前驱。
在这个过程中可能会导致一些其他水平直线上产生多于两个被标记的点,再将这些直线加入待处理的直线集合中重复上述调整,直到合法为止。
显然这个过程最多调整 \(n\) 轮,总时间复杂度 \(O(n\log n)\)
D
丧心病狂
题外话:这道题的 idea 跟 LGMwzs 之前出的一道题撞了。
\(dis(x,y)=d(x)+d(y)-2d(\text{lca}_{x,y})\),其中 \(d(x)\) 是固定的。下面直接给出后面那部分的计算方式。
设 \(a_i=min\{d_x-2d_i\}\),其中 \(x\) 在 \(i\) 某个轻儿子的子树内。
考虑从起点 \(x\) 开始不断往根跳重链。设当前跳到了一条以 \(p\) 为顶的重链,当前位置为 \(z\)。那么 \(\text{lca}_{x,y}\) 在这条重链上的情况有以下几种:
- \(y\) 在 \(z\) 的子树内,直接线段树上区间查询白点的 \(d\) 最小值。
- \(y\) 在这条链的 \([p,z)\) 部分,直接线段树上找这段中白点的 \(d\) 最大值。
- \(y\) 在这条链 \([p,z)\) 这段的某个点轻儿子子树内,直接线段树上找这段的 \(a\) 最小值。
考虑修改边权对 \(a\) 的影响:修改的边下面的子树内就是 \(a\) 整体减,对于子树外,到根的路径上每条重链只会影响一个位置。边跳边更新即可。
反转操作同理,到根的路径上每条重链只会影响一个位置。
\(d\) 是好维护的,注意各种细节。
总时间复杂度 \(O(n\log n+m\log^2n)\)
E
我还不会
代码
A
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=5050,M=10086,V=32;
const long long INF=1e18;
int n,m,K,tot,h[N];
long long dis[N][V],sum[V];
struct edge{int v,w,nxt;}e[M];
struct node{
int p,f;long long w;node(){}
node(int _p,int _f,long long _w){p=_p;f=_f;w=_w;}
bool operator <(const node b)const{return w>b.w;}
};
priority_queue<node>q;
long long Max(long long a,long long b){return a>b?a:b;}
long long Min(long long a,long long b){return a<b?a:b;}
int read(){
char ch=getchar();int nn=0,ssss=1;
while(ch<'0'||ch>'9'){if(ch=='-')ssss*=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){nn=nn*10+(ch-'0');ch=getchar();}
return nn*ssss;
}
bool add(int u,int v,int w){e[++tot].v=v;e[tot].w=w;e[tot].nxt=h[u];h[u]=tot;return true;}
int main(){
n=read();m=read();K=read();
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=K;i++){int p=read()+1;q.push(node(p,0,dis[p][0]=0));}
for(int i=1;i<=m;i++){int u=read()+1,v=read()+1,w=read();add(v,u,w);}
while(!q.empty()){
node np=q.top();q.pop();if(np.w!=dis[np.p][np.f])continue;
for(int i=h[np.p];i;i=e[i].nxt){
if(dis[e[i].v][np.f]>np.w+e[i].w)
q.push(node(e[i].v,np.f,dis[e[i].v][np.f]=np.w+e[i].w));
for(int j=0;j<5;j++){
if((np.f>>j)&1)continue;
int w=e[i].w/10*(9-j),f=np.f|(1<<j);
if(dis[e[i].v][f]>np.w+w)
q.push(node(e[i].v,f,dis[e[i].v][f]=np.w+w));
}
}
}
int Q=read();
while(Q--){
int s=read()+1,U=31;long long ans=INF;
for(int i=0;i<5;i++){sum[1<<i]=read();if(sum[1<<i]<0)U^=1<<i;}
for(int S=1;S<32;S++){
if(__builtin_popcount(S)==1)continue;
sum[S]=sum[S&(S-1)]+sum[S&(-S)];
}
for(int S=U;;S=(S-1)&U){ans=Min(ans,sum[S]+dis[s][S]);if(S==0)break;}
if(ans==INF)puts("-1");
else printf("%lld\n",ans);
}
}
B
#include<cstdio>
#include<algorithm>
const int N=500086,INF=1e9+7;
int n,p[N],x[N],lm[N],pos[N],mx[N<<2],ml[N<<2],mp[N<<2],tag[N<<2],dm[N<<2];
int Min(int a,int b){return a<b?a:b;}
int Max(int a,int b){return a>b?a:b;}
int read(){
char ch=getchar();int nn=0,ssss=1;
while(ch<'0'||ch>'9'){if(ch=='-')ssss*=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){nn=nn*10+(ch-'0');ch=getchar();}
return nn*ssss;
}
bool cmp(int a,int b){return x[a]+lm[a]!=x[b]+lm[b]?x[a]+lm[a]<x[b]+lm[b]:lm[a]<lm[b];}
bool pushup(int k){
mx[k]=Min(mx[k<<1],mx[k<<1|1]);ml[k]=Min(ml[k<<1],ml[k<<1|1]);
mp[k]=(mx[k<<1|1]==mx[k])?mp[k<<1|1]:mp[k<<1];
dm[k]=Min(dm[k<<1],dm[k<<1|1]);return true;
}
bool pushdown(int k,int l,int r){
if(!tag[k])return true;
ml[k<<1]-=tag[k];ml[k<<1|1]-=tag[k];
if(dm[k<<1]!=INF)dm[k<<1]-=tag[k];
if(dm[k<<1|1]!=INF)dm[k<<1|1]-=tag[k];
tag[k<<1]+=tag[k];tag[k<<1|1]+=tag[k];
tag[k]=0;return true;
}
bool build(int k,int l,int r){
if(l==r){mx[k]=x[p[l]];ml[k]=lm[p[l]];mp[k]=p[l];dm[k]=INF;return true;}
int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);return pushup(k);
}
bool erase(int k,int l,int r,int x){
if(l==r){mx[k]=INF;dm[k]=ml[k];return true;}
int mid=(l+r)>>1;pushdown(k,l,r);
if(x<=mid)erase(k<<1,l,mid,x);
else erase(k<<1|1,mid+1,r,x);
return pushup(k);
}
bool change(int k,int l,int r,int x,int y,int z){
if(l>=x&&r<=y){ml[k]-=z;if(dm[k]!=INF)dm[k]-=z;tag[k]+=z;return true;}
int mid=(l+r)>>1;pushdown(k,l,r);
if(x<=mid)change(k<<1,l,mid,x,y,z);
if(mid<y)change(k<<1|1,mid+1,r,x,y,z);
return pushup(k);
}
bool pop(int k,int l,int r){
if(ml[k]>=0)return false;
if(l==r&&ml[k]<0){mx[k]=ml[k]=INF;return true;}
int mid=(l+r)>>1;pushdown(k,l,r);
if(ml[k<<1]<0)pop(k<<1,l,mid);
if(ml[k<<1|1]<0)pop(k<<1|1,mid+1,r);
return pushup(k);
}
int ask(int k,int l,int r,int x,int y){
if(dm[k]==INF||(l>=x&&r<=y))return dm[k];
int mid=(l+r)>>1,ret=INF;pushdown(k,l,r);
if(x<=mid)ret=Min(ret,ask(k<<1,l,mid,x,y));
if(mid<y)ret=Min(ret,ask(k<<1|1,mid+1,r,x,y));
return ret;
}
int main(){
n=read();
for(int i=1;i<=n;i++)x[p[i]=i]=read();
for(int i=1;i<=n;i++)lm[i]=read();
std::sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;i++)pos[p[i]]=i;
build(1,1,n);int ans=0;
while(mx[1]!=INF){
int np=mp[1];erase(1,1,n,pos[np]);
if(pos[np]==n)ans++;
else if(ask(1,1,n,pos[np]+1,n)>=x[np]){
ans++;
change(1,1,n,pos[np]+1,n,x[np]);
}
pop(1,1,n);
}
printf("%d",ans);
}
C
#include<cstdio>
#include<vector>
#include<set>
#include<algorithm>
using std::vector;
using std::set;
const int N=1000086;
int n,tp,x[N],y[N],mn[N],mx[N],pos[N],st[N];
bool vis[N];
struct infor{
int x,p;infor(){}infor(int _x,int _p){x=_x;p=_p;}
bool operator <(const infor b)const{return x<b.x;}
};
vector<int>vx[N],vy[N];
set<infor>s[N];
int read(){
char ch=getchar();int nn=0,ssss=1;
while(ch<'0'||ch>'9'){if(ch=='-')ssss*=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){nn=nn*10+(ch-'0');ch=getchar();}
return nn*ssss;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
x[i]=read();y[i]=read();
vx[x[i]].push_back(i);
vy[y[i]].push_back(i);
}
for(int i=1;i<=1000000;i++){
std::sort(vx[i].begin(),vx[i].end(),[&](int a,int b){return y[a]<y[b];});
std::sort(vy[i].begin(),vy[i].end(),[&](int a,int b){return x[a]<x[b];});
for(int j=0;j<(int)vx[i].size();j++)pos[vx[i][j]]=j;
if(vx[i].size()==1){
mn[i]=mx[i]=vx[i].back();
s[y[mn[i]]].insert(infor(x[mn[i]],mn[i]));
}
if(vx[i].size()>1){
mn[i]=vx[i].front();mx[i]=vx[i].back();
s[y[mn[i]]].insert(infor(x[mn[i]],mn[i]));
s[y[mx[i]]].insert(infor(x[mx[i]],mx[i]));
}
}
for(int i=1;i<=1000000;i++)if(s[i].size()>2){st[++tp]=i;vis[i]=true;}
while(tp){
int ny=st[tp--];vis[ny]=false;if(s[ny].size()<3)continue;
set<infor>::iterator il=next(s[ny].begin()),ir=prev(s[ny].end());
for(set<infor>::iterator it=il;it!=ir;it++){
int nx=it->x,np=it->p;
if(np==mn[nx]&&np==mx[nx]){mn[nx]=mx[nx]=0;continue;}
else if(np==mn[nx]){
mn[nx]=vx[nx][pos[np]+1];int rp=mn[nx];
s[y[rp]].insert(infor(nx,rp));
if(s[y[rp]].size()>2&&vis[y[rp]]==false)vis[st[++tp]=y[rp]]=true;
}
else if(np==mx[nx]){
mx[nx]=vx[nx][pos[np]-1];int rp=mx[nx];
s[y[rp]].insert(infor(nx,rp));
if(s[y[rp]].size()>2&&vis[y[rp]]==false)vis[st[++tp]=y[rp]]=true;
}
}
for(set<infor>::iterator it=il;it!=ir;){
set<infor>::iterator lst=it;
it=next(it);s[ny].erase(lst);
}
}
for(int i=1;i<=1000000;i++)vis[mn[i]]=vis[mx[i]]=true;
for(int i=1;i<=n;i++)putchar('0'+vis[i]);
}
D
#include<cstdio>
#include<cstring>
const int N=100086;
const long long INF=1e18;
int n,m,tot,dfncnt,h[N],dfl[N],dfr[N],dfa[N],tp[N],fa[N],hs[N],sze[N],ew[N],dep[N];
long long d[N<<2],md[N<<2],td[N<<2],ma[N<<2],ta[N<<2];
struct edge{int v,w,nxt;}e[N<<1];
long long Max(long long a,long long b){return a>b?a:b;}
long long Min(long long a,long long b){return a<b?a:b;}
int read(){
char ch=getchar();int nn=0,ssss=1;
while(ch<'0'||ch>'9'){if(ch=='-')ssss*=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){nn=nn*10+(ch-'0');ch=getchar();}
return nn*ssss;
}
bool add(int u,int v,int w){e[++tot].v=v;e[tot].w=w;e[tot].nxt=h[u];h[u]=tot;return true;}
bool cdis(int,int,int,int,int,long long);
int dfs1(int np,int lst){//树剖1
fa[np]=lst;dep[np]=dep[lst]+1;sze[np]=1;
for(int i=h[np];i;i=e[i].nxt){
if(e[i].v==lst)continue;
ew[e[i].v]=e[i].w;sze[np]+=dfs1(e[i].v,np);
if(sze[e[i].v]>sze[hs[np]])hs[np]=e[i].v;
}
return sze[np];
}
bool dfs2(int np,int ntp){//树剖2
dfa[dfl[np]=++dfncnt]=np;tp[np]=ntp;if(hs[np])dfs2(hs[np],ntp);
for(int i=h[np];i;i=e[i].nxt){
if(e[i].v==fa[np]||e[i].v==hs[np])continue;
dfs2(e[i].v,e[i].v);
}
if(np!=1)cdis(1,1,n,dfl[np],dfncnt,ew[np]);
dfr[np]=dfncnt;return true;
}
bool pushdown(int k){
if(td[k]){
d[k<<1]+=td[k];td[k<<1]+=td[k];if(md[k<<1]!=INF)md[k<<1]+=td[k];
d[k<<1|1]+=td[k];td[k<<1|1]+=td[k];if(md[k<<1|1]!=INF)md[k<<1|1]+=td[k];
}
if(ta[k]){
if(ma[k<<1]!=INF){ma[k<<1]+=ta[k];ta[k<<1]+=ta[k];}
if(ma[k<<1|1]!=INF){ma[k<<1|1]+=ta[k];ta[k<<1|1]+=ta[k];}
}
td[k]=ta[k]=0;return true;
}
bool pushup(int k){
md[k]=Min(md[k<<1],md[k<<1|1]);ma[k]=Min(ma[k<<1],ma[k<<1|1]);
return true;
}
bool build(int k,int l,int r){
md[k]=ma[k]=INF;if(l==r)return true;int mid=(l+r)>>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);return true;
}
bool cdis(int k,int l,int r,int x,int y,long long w){//深度,区间加
if(l>=x&&r<=y){if(md[k]!=INF)md[k]+=w;d[k]+=w;td[k]+=w;return true;}
int mid=(l+r)>>1;pushdown(k);
if(x<=mid)cdis(k<<1,l,mid,x,y,w);
if(mid<y)cdis(k<<1|1,mid+1,r,x,y,w);
return pushup(k);
}
long long gdis(int k,int l,int r,int x){
if(l==r)return d[k];int mid=(l+r)>>1;pushdown(k);
return x<=mid?gdis(k<<1,l,mid,x):gdis(k<<1|1,mid+1,r,x);
}
long long qdis(int k,int l,int r,int x,int y){//可用点的深度区间min
if(l>=x&&r<=y)return md[k];
int mid=(l+r)>>1;long long ret=INF;pushdown(k);
if(x<=mid)ret=qdis(k<<1,l,mid,x,y);
if(mid<y)ret=Min(ret,qdis(k<<1|1,mid+1,r,x,y));
return ret;
}
long long gmx(int k,int l,int r,int x,int y){//查询链上深度最大可用点的深度
if(md[k]==INF)return INF;if(l==r)return md[k];int mid=(l+r)>>1;pushdown(k);
if(l>=x&&r<=y)return md[k<<1|1]==INF?gmx(k<<1,l,mid,x,y):gmx(k<<1|1,mid+1,r,x,y);
if(mid<y){long long ret=gmx(k<<1|1,mid+1,r,x,y);if(ret!=INF)return ret;}
if(x<=mid)return gmx(k<<1,l,mid,x,y);return INF;
}
bool cwa(int k,int l,int r,int x,int y,long long a){//ai=min{dx-2di},x在i轻儿子子树内,区间加
if(l>=x&&r<=y){if(ma[k]!=INF){ma[k]+=a;ta[k]+=a;}return true;}
int mid=(l+r)>>1;pushdown(k);
if(x<=mid)cwa(k<<1,l,mid,x,y,a);
if(mid<y)cwa(k<<1|1,mid+1,r,x,y,a);
return pushup(k);
}
bool awa(int k,int l,int r,int x,long long a){//a单点赋值
if(l==r){ma[k]=a;return true;}int mid=(l+r)>>1;pushdown(k);
if(x<=mid)awa(k<<1,l,mid,x,a);else awa(k<<1|1,mid+1,r,x,a);
return pushup(k);
}
long long qwa(int k,int l,int r,int x,int y){//青蛙。min{a}
if(l>=x&&r<=y)return ma[k];
int mid=(l+r)>>1;long long ret=INF;pushdown(k);
if(x<=mid)ret=qwa(k<<1,l,mid,x,y);
if(mid<y)ret=Min(ret,qwa(k<<1|1,mid+1,r,x,y));
return ret;
}
bool rev(int k,int l,int r,int x){//反转dfn为x的点的状态
if(l==r){if(md[k]==d[k])md[k]=INF;else md[k]=d[k];return md[k]==d[k];}
int mid=(l+r)>>1,ret=1;pushdown(k);
ret=(x<=mid)?rev(k<<1,l,mid,x):rev(k<<1|1,mid+1,r,x);
pushup(k);return ret;
}
bool upd(int x){
while(fa[tp[x]]){
int y=fa[tp[x]];long long pa=qwa(1,1,n,dfl[y],dfl[y]);
long long nd=qdis(1,1,n,dfr[hs[y]]+1,dfr[y]);
if(nd==INF&&pa!=INF)awa(1,1,n,dfl[y],INF);
if(nd!=INF)awa(1,1,n,dfl[y],nd-2*gdis(1,1,n,dfl[y]));
x=y;
}
return true;
}
bool Q(int x){//查询可用点到x的最短距离
int sp=x;long long ret=INF;
while(x){
int y=tp[x];long long nans=qdis(1,1,n,dfl[x],dfr[x]);
if(nans!=INF)ret=Min(ret,-2*gdis(1,1,n,dfl[x])+nans);
nans=gmx(1,1,n,dfl[y],dfl[x]);
if(nans!=INF)ret=Min(ret,-nans);
ret=Min(ret,qwa(1,1,n,dfl[y],dfl[x]));
x=fa[y];
}
if(ret==INF)puts("-1");
else printf("%lld\n",gdis(1,1,n,dfl[sp])+ret);
return ret;
}
bool R(int x){//反转x的状态
rev(1,1,n,dfl[x]);return upd(x);
}
bool C(int a,int b,int w){//修改(a,b)边权
if(fa[a]==b)a^=b^=a^=b;
cdis(1,1,n,dfl[b],dfr[b],w-ew[b]);
cwa(1,1,n,dfl[b],dfr[b],ew[b]-w);
ew[b]=w;return upd(b);
}
int main(){
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
n=read();m=read();
for(int i=1;i<n;i++){
int u=read();int v=read();int w=read();
add(u,v,w);add(v,u,w);
}
build(1,1,n);dfs1(1,0);dfs2(1,1);
for(int o=1;o<=m;o++){
int opt=read();
if(opt==1)Q(read());
if(opt==2)R(read());
if(opt==3){int a=read();int b=read();int w=read();C(a,b,w);}
}
}
E
待补