【樹鏈剖分】權值取反,區間最大
阿新 • • 發佈:2019-02-16
尋找 name 之間 一個數 sign 數據 min swa 序列
【題面】
有一棵n個點的樹,邊按照1~n-1標號,每條邊擁有一個邊權 現在有 m 次操作,每次操作為如下三種之一:
. 1 x y:邊x的權值改為y
. 2 x y:將點x到點y路徑上的所有邊權值變成相反數
. 3 x y:查詢點x到點y路徑上的最大邊權
第一行為兩個整數n,m,表示序列長度和操作次數
接下來n-1行,每行三個數a,b,v,表示a,b之間有一條權值為v的邊,按照標號1~n-1的順序給出
接下來m行, 每行以1/2/3作為第一個數,表示操作種類。接下來兩個整數,格式如題,表示一次操作
對於每個3操作,輸出一行一個整數,表示詢問的答案
樣例輸入
3 3
1 2 3
2 3 2
3 1 3
2 1 3
3 1 3
樣例輸出
3
-2
約定
對於100%的數據n,<=1e5,給出的所有數絕對值不超過1e9,且保證操作均合法
幾乎就是個裸的樹剖模板,多了一個異或(xor)控制的取反標記,每次線段樹標記下傳,維護一下
這題居然花了我三個小時qwq
#include<bits/stdc++.h> #define fo(i,j,k) for(register int i=j;i<=k;i++) #define N 100005 #define lson l,mid,x<<1 #define rson mid+1,r,x<<1|1 using namespace std; int n,m,x,y,v; vector<int> to[N],val[N]; int fa[N],deep[N],size[N],son[N],top[N],p[N];//樹鏈剖分 int tot,maxx[N<<2],minn[N<<2],X[N],Y[N],a[N],rank[N]; bool flag[N<<2];//是否取反 int max(int a,int b) {return a>b ? a:b;} int min(int a,int b) {return a<b ? a:b;} void swap(int &a,int &b) {int t=a;a=b;b=t;} //重載 void dfs1(int x,int f,int dep) { fa[x]=f,deep[x]=dep,size[x]=1; int y,ms=0; for(int i=0;i<to[x].size();i++) { if((y=to[x][i])!=f) { dfs1(y,x,dep+1); size[x]+=size[y]; a[y]=val[x][i]; if(size[y]>ms) ms=size[y],son[x]=y; } } } void dfs2(int x,int f,int topf) { p[x]=++tot;rank[tot]=x;top[x]=topf; if(son[x]) dfs2(son[x],x,topf); int y; for(int i=0;i<to[x].size();i++) if((y=to[x][i])!=f&&y!=son[x]) dfs2(y,x,y); } void fan(int x) { flag[x]=flag[x]^1; swap(maxx[x],minn[x]); maxx[x]=-maxx[x],minn[x]=-minn[x]; }//取相反數 void Pushup(int x) { maxx[x]=max(maxx[x<<1],maxx[x<<1|1]); minn[x]=min(minn[x<<1],minn[x<<1|1]); }//維護 void Pushdown(int x) {if(flag[x]) {fan(x<<1);fan(x<<1|1);flag[x]=0;}} //取反標記下放 void build(int l,int r,int x) { flag[x]=0; if(l==r) maxx[x]=minn[x]=a[rank[l]]; else { int mid=(l+r)>>1; build(lson);build(rson); Pushup(x); } }//建樹 void update(int l,int r,int x,int st,int en) { if(st<=l&&r<=en) fan(x); else { int mid=(l+r)>>1; Pushdown(x); if(en<=mid) update(lson,st,en); else if(st>mid) update(rson,st,en); else update(lson,st,en),update(rson,st,en); Pushup(x); } } void change1(int l,int r,int x,int k,int val) { if(l==r) maxx[x]=minn[x]=val; else { int mid=(l+r)>>1; Pushdown(x); if(k<=mid) change1(lson,k,val); else change1(rson,k,val); Pushup(x); } }//單點修改 int query(int l,int r,int x,int st,int en) { if(st<=l&&r<=en) return maxx[x]; else { int mid=(l+r)>>1; Pushdown(x); if(en<=mid) return query(lson,st,en); else if(st>mid) return query(rson,st,en); else return max(query(lson,st,en),query(rson,st,en)); } } void change2(int x,int y) { while(top[x]!=top[y]) { if(deep[top[x]]<deep[top[y]]) swap(x,y); update(1,n,1,p[top[x]],p[x]); x=fa[top[x]]; } if(x==y) return; if(deep[x]<deep[y]) swap(x,y); update(1,n,1,p[son[y]],p[x]); }//區間取反 int find(int x,int y) { int ans=-1234567890; while(top[x]!=top[y]) { if(deep[top[x]]<deep[top[y]]) swap(x,y); ans=max(ans,query(1,n,1,p[top[x]],p[x])); x=fa[top[x]]; } if(x==y) return ans; if(deep[x]<deep[y]) swap(x,y); ans=max(ans,query(1,n,1,p[son[y]],p[x])); return ans; }//尋找區間最大值 template<class T> inline void read(T &re) { re=0;T sign=1;char tmp; while((tmp=getchar())&&(tmp<'0'||tmp>'9')) if(tmp=='-') sign=-1;re=tmp-'0'; while((tmp=getchar())&&(tmp>='0'&&tmp<='9')) re=re*10+(tmp-'0');re*=sign; } int main() { freopen("max.in","r",stdin); freopen("max.out","w",stdout); int kkksc03; read(n);read(m); fo(i,1,n-1) { read(x);read(y);read(v); to[x].push_back(y); val[x].push_back(v); to[y].push_back(x); val[y].push_back(v); X[i]=x;Y[i]=y; } dfs1(1,0,1);dfs2(1,0,1);build(1,n,1); while(m--) { read(kkksc03);read(x);read(y); if (kkksc03==1) { if (deep[X[x]]>deep[Y[x]]) change1(1,n,1,p[X[x]],y); else change1(1,n,1,p[Y[x]],y); } if (kkksc03==2) change2(x,y); if (kkksc03==3) printf("%d\n",find(x,y)); } return 0; } /* 3 3 1 2 3 2 3 2 3 1 3 2 1 3 3 1 3 (3 -2) 8 6 1 2 3 1 5 8 2 3 9 2 4 7 5 8 1 3 6 6 4 7 2 1 4 -5 2 1 7 3 2 6 3 1 7 2 1 8 3 1 5 (9 5 -8) */
看到右邊的打賞了嗎,你可以給我錢讓我去買冰闊落!qwq
【樹鏈剖分】權值取反,區間最大