AC BZOJ1036 裸的树链模板
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <list>
typedef unsigned int uint;
typedef long long int ll;
typedef unsigned long long int ull;
typedef double db;
using namespace std;
inline int getint()
{
int res=0;
char c=getchar();
bool mi=false;
while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
return mi ? -res : res;
}
const int INF=(1<<30)-1;
int n;
int a[30050];
//Segment Tree
ll sum[240000];
int mx[240000];
void update(const int&x)
{
sum[x]=sum[x<<1]+sum[x<<1|1];
mx[x]=max(mx[x<<1],mx[x<<1|1]);
}
void Build(const int&x=1,const int&l=0,const int&r=n-1)
{
if(l==r) { mx[x]=sum[x]=a[l]; return ; }
int mid=(l+r)>>1;
Build(x<<1,l,mid);
Build(x<<1|1,mid+1,r);
update(x);
}
int cp,cv;
void Change(const int&x=1,const int&l=0,const int&r=n-1)
{
if(cp<l || r<cp) return ;
if(l==r) { mx[x]=sum[x]=cv; return ; }
int mid=(l+r)>>1;
Change(x<<1,l,mid);
Change(x<<1|1,mid+1,r);
update(x);
}
int ql,qr;
int QueryMax(const int&x=1,const int&l=0,const int&r=n-1)
{
if(qr<l || r<ql) return -INF;
if(ql<=l && r<=qr) return mx[x];
int mid=(l+r)>>1;
return max(
QueryMax(x<<1,l,mid),
QueryMax(x<<1|1,mid+1,r));
}
ll QuerySum(int x=1,int l=0,int r=n-1)
{
if(qr<l || r<ql) return 0;
if(ql<=l && r<=qr) return sum[x];
int mid=(l+r)>>1;
return QuerySum(x<<1,l,mid) + QuerySum(x<<1|1,mid+1,r);
}
//End of Segment Tree
struct edge
{
int in;
edge*nxt;
}pool[80000];
edge*et=pool;
edge*eds[30050];
void addedge(int a,int b)
{
et->in=a; et->nxt=eds[b]; eds[b]=et++;
et->in=b; et->nxt=eds[a]; eds[a]=et++;
}
#define FOREACH_EDGE(i,j) for(edge*i=eds[j];i;i=i->nxt)
int c[30050],ctot; //chain code
int h[30050]; //head node of chain this node stands
bool t[30050]; //is this node a tail of a chain?
int f[30050]; //father node
int id[30050]; //location in segment
int dep[30050]; //depth
int DFS(int x,int cd)
{
dep[x]=cd;
int sum=0;
int m=0;
int p=-1;
FOREACH_EDGE(i,x)
if(i->in!=f[x])
{
f[i->in]=x;
int h=DFS(i->in,cd+1);
sum+=h;
if(m<h)
{
m=h;
p=i->in;
}
}
if(p==-1) c[x]=ctot++,t[x]=true; //start a new chain
else c[x]=c[p]; //Insert this node to a chain
return sum+1;
}
inline int GetMax(int a,int b)
{
int res=-INF;
while(c[a]!=c[b])
{
if(dep[h[c[a]]]<dep[h[c[b]]]) swap(a,b);
int&top=h[c[a]];
ql=id[a];
qr=id[top];
res=max(res,QueryMax());
a=f[top];
}
ql=min(id[a],id[b]);
qr=max(id[a],id[b]);
res=max(res,QueryMax());
return res;
}
inline ll GetSum(int a,int b)
{
ll res=0;
while(c[a]!=c[b])
{
if(dep[h[c[a]]]<dep[h[c[b]]]) swap(a,b);
int&top=h[c[a]];
ql=id[a];
qr=id[top];
res+=QuerySum();
a=f[top];
}
ql=min(id[a],id[b]);
qr=max(id[a],id[b]);
res+=QuerySum();
return res;
}
inline void Edit(int a,ll p)
{
cp=id[a];
cv=p;
Change();
}
int main()
{
n=getint();
for(int i=0;i<n-1;i++)
addedge(getint()-1,getint()-1);
f[0]=0;
DFS(0,0);
//assign nodes to the segment tree
int base=0;
for(int i=0;i<n;i++)
if(t[i])
{
int x=i;
while(true)
{
id[x]=base++;
if(c[f[x]]==c[x] && x!=0) x=f[x];
else break;
}
h[c[x]]=x;
}
for(int i=0;i<n;i++)
a[id[i]]=getint();
Build();
//deal all query
int q=getint();
for(int d=0;d<q;d++)
{
char inp[8];
scanf("%s",inp);
switch(inp[3])
{
case 'X': //QMAX
{
int l=getint()-1;
int r=getint()-1;
printf("%d\n",GetMax(l,r));
}
break;
case 'M': //QSUM
{
int l=getint()-1;
int r=getint()-1;
printf("%lld\n",GetSum(l,r));
}
break;
case 'N': //CHANGE
{
int p=getint()-1;
int v=getint();
Edit(p,v);
}
break;
default:break;
}
}
return 0;
}
照着这个写!
写树链的大致思路:
1.线段树.
一棵全局线段树.......虽然牺牲了常数...但是至少不用去动态开树了=w=
2.构造轻重边.
这个是重点.
首先DFS记下: 1.父节点 2.是不是叶节点 3.所属链号
如果当前节点是叶节点,那么新开一条链.
如果当前节点不是叶节点,那么此节点所树链为节点最多的子树的根所属的链.
然后在DFS之外,用两层循环标记: 1.链头 2.每个节点在线段树中的位置.
一直从叶节点跑上去即可. 显然有多少个叶节点就有多少条链.
3.查询操作
查询(a,b)时保证a与b中a的所属链深度大于b的所属链深度.然后询问a到链头的值.
如果是边权....嗯.....我习惯把边权压到深度较大的那个节点......于是询问的时候:
链询问:询问a到链头的值; 然后再询问链头到它父节点(就是它本身,这个时候没必要理会)的值.
当a,b同属一条链时,还是令a的深度大于b的深度,然后询问a到b的前一个节点的值.