定义:
1.重边:记 Size(U ) 表示以 U 为根的子树的结点个数,令 V 为 U 的儿子中
Size 最大的一个,那么我们称边 (U ,V ) 为重边,其余边为轻边。
2.重链:全都由重边组成的链称为重链。轻链同理定义
性质:
1.如果 (U ,V ) 为轻边,则
Size(V)<Size(U)/2
2.从根到某一点的路径上轻边的个数不大于 O(log N )
3.从根到某一点的路径上重链的条数不大于O(logN)
由以上性质可以将链
<u,v>
<script type="math/tex" id="MathJax-Element-2">
</script>分成O(logN)条轻边和O(logN)条重链,用线段树维护重链的性质,直接遍历轻边。
SPOJ QTREE
题意:点修改边权,链询问边权最大值
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<string.h>
#include<math.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn = 20000+10;
int t,n;
char ch[10];
int a[maxn],b[maxn],c[maxn];
struct node{ int v,nx; }e[maxn<<2];
int hd[maxn],tt;
void add(int u,int v){
e[tt].v=v,e[tt].nx=hd[u],hd[u]=tt++;
}
//init
int f[maxn],son[maxn],num[maxn],tp[maxn],id[maxn],d[maxn],p;
void dfs1(int u,int fa,int dep){
num[u]=1,f[u]=fa,d[u]=dep;
int tn=0;
for(int i=hd[u];i!=-1;i=e[i].nx){
int v=e[i].v;
if(v==fa) continue;
dfs1(v,u,dep+1);
num[u]+=num[v];
if(num[v]>tn) { tn=num[v],son[u]=v; }
}
}
void dfs2(int u,int tpu){
tp[u]=tpu,id[u]=p++;
if(son[u]!=-1) dfs2(son[u],tpu);
for(int i=hd[u];i!=-1;i=e[i].nx){
int v=e[i].v;
if(v==f[u]||v==son[u]) continue;
dfs2(v,v);
}
}
void init(){
mem(hd,-1),tt=p=0,mem(son,-1);
}
//Segment tree
int Max[maxn<<2];
void pushup(int rt){ Max[rt]=max(Max[rt<<1],Max[rt<<1|1]); }
void build(int l,int r,int rt){
if(l==r) { Max[rt]=0; return; }
int m=(l+r)>>1;
build(lson),build(rson);
pushup(rt);
}
void update(int p,int x,int l,int r,int rt){
if(l==r) { Max[rt]=x; return; }
int m=(l+r)>>1;
if(p<=m) update(p,x,lson);
else update(p,x,rson);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R) return Max[rt];
int m=(l+r)>>1,tmp=0;
if(L<=m) tmp=max(tmp,query(L,R,lson));
if(R>m) tmp=max(tmp,query(L,R,rson));
return tmp;
}
//query
int Find(int u,int v){
int uu=tp[u],vv=tp[v],tans=0;
while(uu!=vv){
if(d[uu]<d[vv]) swap(uu,vv),swap(u,v);
tans=max(tans,query(id[uu],id[u],0,p-1,1));
u=f[uu],uu=tp[u];
}
if(u==v) return tans;
if(d[u]>d[v]) swap(u,v);
return max(tans,query(id[son[u]],id[v],0,p-1,1));
}
int main(){
//freopen("a.txt","r",stdin);
scanf("%d",&t);
while(t--){
scanf("%d",&n);
init();
for(int i=1;i<n;i++){
scanf("%d%d%d",&a[i],&b[i],&c[i]);
add(a[i],b[i]),add(b[i],a[i]);
}
dfs1(1,0,1);
dfs2(1,1);
build(0,p-1,1);
for(int i=1;i<n;i++){
if(d[a[i]]<d[b[i]]) swap(a[i],b[i]);
update(id[a[i]],c[i],0,p-1,1);
}
while(scanf("%s",ch)!=EOF){
if(ch[0]=='D') break;
int x,y; scanf("%d%d",&x,&y);
if(ch[0]=='C') update(id[a[x]],y,0,p-1,1);
else printf("%d\n",Find(x,y));
}
}
return 0;
}
hdu5458
题意:在一个n边m点无向图(含重边和自环)中给出两种操作:
1.删除一条边
2.询问任意两点u和v的割边数量
解法:先找出无向图的dfs树,对于非树边,可以考虑成在树中添边的过程。添加一条边后,那么整条链上的边都不再是割边了。如果把后面的删边改成添边,那么恰好这两种操作就统一为添边操作。题意叙述改成:在一棵n点的树中给出两种操作:
1.添加一条边
2.询问任意两点u和v的割边数量
先将所有边赋值为1,添加
#include<iostream>
#include<cstdio>
#include<cstring>
#define N maxn
#define PII pair<int,int>
#include<map>
#include<vector>
#define MP make_pair
#include<map>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn = 100000+10;
int t,n,m,q;
struct node{
int v,nxt;
}e[maxn<<2];
int head[maxn],tot;
map<PII,int> ma;
int a[maxn],b[maxn];
int x[maxn],y[maxn],z[maxn];
int bri[2][maxn],cnt;
int vis[maxn],f[maxn],son[maxn],num[maxn],tp[maxn],id[maxn],d[maxn],p;
int ans[maxn],cnt2;
void add(int u,int v){
e[tot].v=v,e[tot].nxt=head[u],head[u]=tot++;
}
//Segment Tree
int Sum[maxn<<2],cg[maxn<<2];
void pushup(int rt) { Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1]; }
void pushdown(int rt,int l,int r){
if(l==r) return;
if(cg[rt]!=-1){
int m=(l+r)>>1;
cg[rt<<1]=cg[rt<<1|1]=cg[rt];
Sum[rt<<1]=(m-l+1)*cg[rt];
Sum[rt<<1|1]=(r-m)*cg[rt];
cg[rt]=-1;
}
}
void build(int l,int r,int rt){
cg[rt]=-1;
if(l==r) { Sum[rt]=1; return; }
int m=(l+r)>>1;
build(lson),build(rson);
pushup(rt);
}
void change(int L,int R,int x,int l,int r,int rt){
if(L<=l&&r<=R){
cg[rt]=x,Sum[rt]=(r-l+1)*x;
return;
}
pushdown(rt,l,r);
int m=(l+r)>>1;
if(L<=m) change(L,R,x,lson);
if(R>m) change(L,R,x,rson);
pushup(rt);
}
int qsum(int L,int R,int l,int r,int rt){
int sum=0;
if(L<=l&&r<=R) return Sum[rt];
pushdown(rt,l,r);
int m=(l+r)>>1;
if(L<=m) sum+=qsum(L,R,lson);
if(R>m) sum+=qsum(L,R,rson);
return sum;
}
//L_W(树链剖分)
void solvechange(int u,int v,int x){
while(tp[u]!=tp[v]){
if(d[tp[u]]>d[tp[v]]) swap(u,v);
change(id[tp[v]],id[v],x,1,n,1);
v=f[tp[v]];
}
if(u==v) return;
if(d[u]>d[v]) swap(u,v);
change(id[son[u]],id[v],x,1,n,1);
}
int solvesum(int u,int v){
int sm=0;
while(tp[u]!=tp[v]){
if(d[tp[u]]>d[tp[v]]) swap(u,v);
sm+=qsum(id[tp[v]],id[v],1,n,1);
v=f[tp[v]];
}
if(u==v) return sm;
if(d[u]>d[v]) swap(u,v);
sm+=qsum(id[u]+1,id[v],1,n,1);
return sm;
}
void dfs1(int u,int fa,int dep){
num[u]=1,f[u]=fa,d[u]=dep,vis[u]=1;
int tn=0;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(v==fa) continue;
if(vis[v]) { bri[0][cnt]=u,bri[1][cnt++]=v; continue; }//记录非树边
dfs1(v,u,dep+1);
num[u]+=num[v];
if(num[v]>tn) { tn=num[v],son[u]=v; }
}
}
void dfs2(int u,int tpu){
tp[u]=tpu,id[u]=++p;
if(son[u]!=-1) dfs2(son[u],tpu);
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(f[v]!=u||v==son[u]) continue;//非重链儿子节点
dfs2(v,v);
}
}
int main(){
//freopen("a.txt","r",stdin);
scanf("%d",&t);
int cas=0;
while(t--){
ma.clear();
scanf("%d%d%d",&n,&m,&q);
memset(head,-1,sizeof(head)); tot=0;
memset(son,-1,sizeof(son));
p=0;
memset(vis,0,sizeof(vis));
cnt=0;
for(int i=1;i<=m;i++){
scanf("%d%d",&a[i],&b[i]);
if(a[i]==b[i]) continue;
if(a[i]>b[i]) swap(a[i],b[i]);
if(ma.find(MP(a[i],b[i]))==ma.end()) ma[MP(a[i],b[i])]=1;
else ma[MP(a[i],b[i])]++;
}
for(int i=1;i<=q;i++){
scanf("%d%d%d",&x[i],&y[i],&z[i]);
if(y[i]==z[i]) continue;
if(y[i]>z[i]) swap(y[i],z[i]);
if(x[i]==1) ma[MP(y[i],z[i])]--;
}
for(int i=1;i<=m;i++){
if(a[i]==b[i]) continue;
int tmp=ma[MP(a[i],b[i])];
if(tmp==0||tmp==-1) continue;//被删除或者已添加
else{
add(a[i],b[i]),add(b[i],a[i]);
if(tmp>1) bri[0][cnt]=a[i],bri[1][cnt++]=b[i];//记录重边
ma[MP(a[i],b[i])]=-1;
}
}
printf("Case #%d:\n",++cas);
build(1,n,1);
dfs1(1,0,0),dfs2(1,1);
for(int i=0;i<cnt;i++){
int u=bri[0][i],v=bri[1][i];
if(u==v) continue;
solvechange(u,v,0);
}
cnt2=0;
for(int i=q;i>=1;i--){
if(x[i]==1){
if(y[i]==z[i]) continue;
solvechange(y[i],z[i],0);
}
else{
if(y[i]==z[i]) ans[cnt2++]=0;
else{
ans[cnt2++]=solvesum(y[i],z[i]);
}
}
}
for(int i=cnt2-1;i>=0;i--) printf("%d\n",ans[i]);
}
return 0;
}
bzoj4034
题意:树上支持三种操作:
1.修改点权值
2.修改子树点权值
3.询问点到跟节点路径权值和
解法:记录mx[u]:u子树点的最大标号,修改子树权值相当于修改[id[u],mx[u]]区间点权值
注意与边权不同,判断u==v时,需要加上该点的权值!
#include<iostream>
#include<cstdio>
#include<cstring>
#define N maxn
#define PII pair<int,int>
#include<map>
#include<vector>
#define MP make_pair
#include<map>
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5+10;
int n,m;
vector<int> g[maxn];
int w[maxn];
//Segment_Tree
ll ad[maxn<<2],Sum[maxn<<2];
void pushup(int rt){ Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1]; }
void pushdown(int rt,int len){
if(len==1) return;
if(ad[rt]){
ad[rt<<1]+=ad[rt],ad[rt<<1|1]+=ad[rt];
int l=(len+1)/2,r=len/2;
Sum[rt<<1]+=ad[rt]*l;
Sum[rt<<1|1]+=ad[rt]*r;
ad[rt]=0;
}
}
void build(int l,int r,int rt){
ad[rt]=0;
if(l==r) { Sum[rt]=0; return; }
int m=(l+r)>>1;
build(lson),build(rson);
pushup(rt);
}
void add(int L,int R,int x,int l,int r,int rt){
if(L<=l&&r<=R) { ad[rt]+=x,Sum[rt]=(Sum[rt]+(ll)x*(ll)(r-l+1)); return; }
pushdown(rt,r-l+1);
int m=(l+r)>>1;
if(L<=m) add(L,R,x,lson);
if(R>m) add(L,R,x,rson);
pushup(rt);
}
ll qsum(int L,int R,int l,int r,int rt){
pushdown(rt,r-l+1);
ll sm=0;
if(L<=l&&r<=R) return Sum[rt];
int m=(l+r)>>1;
if(L<=m) sm+=qsum(L,R,lson);
if(R>m) sm+=qsum(L,R,rson);
return sm;
}
//L_W
int f[maxn],num[maxn],d[maxn],tp[maxn],id[maxn],mx[maxn],son[maxn],p;
void dfs1(int u,int fa,int dep){
num[u]=1,f[u]=fa,d[u]=dep;
//printf("%d %d\n",u,f[u]);
int tn=0;
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(v==fa) continue;
dfs1(v,u,dep+1);
num[u]+=num[v];
if(num[v]>tn) { tn=num[v],son[u]=v; }
}
}
void dfs2(int u,int tpu){
tp[u]=tpu,id[u]=mx[u]=++p;
if(son[u]!=-1) { dfs2(son[u],tpu); mx[u]=max(mx[u],mx[son[u]]); }
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(v==f[u]||v==son[u]) continue;
dfs2(v,v);
mx[u]=max(mx[u],mx[v]);
}
}
ll solvesum(int u,int v){
ll sm=0;
while(tp[u]!=tp[v]){
if(d[tp[u]]>d[tp[v]]) swap(u,v);//比较tp的深度大小!!
sm+=qsum(id[tp[v]],id[v],1,n,1);
v=f[tp[v]];
}
if(u==v) { sm+=qsum(id[u],id[u],1,n,1); return sm; }//点权需要加上相同点的点权!!
if(d[u]>d[v]) swap(u,v);
sm+=qsum(id[u],id[v],1,n,1);
return sm;
}
int main(){
//freopen("a.txt","r",stdin);
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;i++) { scanf("%d",&w[i]); g[i].clear(); }
int kind,u,v;
for(int i=1;i<=n-1;i++){
scanf("%d%d",&u,&v);
g[u].push_back(v),g[v].push_back(u);
}
memset(son,-1,sizeof(son)); p=0;
build(1,n,1);
dfs1(1,0,0),dfs2(1,1);
for(int i=1;i<=n;i++) add(id[i],id[i],w[i],1,n,1);
while(m--){
scanf("%d",&kind);
if(kind==1){
scanf("%d%d",&u,&v);
add(id[u],id[u],v,1,n,1);
}
else if(kind==2){
scanf("%d%d",&u,&v);
add(id[u],mx[u],v,1,n,1);
}
else{
scanf("%d",&u);
printf("%lld\n",solvesum(1,u));
}
}
}
return 0;
}