1036: [ZJOI2008]树的统计Count
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 7051 Solved: 2872
[ Submit][ Status][ Discuss]
Description
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身
Input
输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。
Output
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
Sample Input
4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4
Sample Output
4
1
2
2
10
6
5
6
5
16
1
2
2
10
6
5
6
5
16
HINT
Source
思路:因为操作的是点权。所以可以直接把其中一个变成根,然后接着搞,要是边权的话,就不行了,就要按照LCA那样高了
这个题也可以用树链剖分写
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=30010;
const int INF=1000000000;
int N,Q;
struct node
{
int v,next;
}edge[maxn*2];
int head[maxn];
int tot;
void add_edge(int u,int v)
{
edge[tot].v=v;
edge[tot].next=head[u];
head[u]=tot++;
}
struct LCT
{
int ch[maxn][2],pre[maxn],key[maxn];
int rev[maxn],maxv[maxn];
bool rt[maxn];
int sum[maxn];
void update_rev(int r)
{
if(!r)return;
swap(ch[r][0],ch[r][1]);
rev[r]^=1;
}
void pushdown(int r)
{
if(rev[r])
{
update_rev(ch[r][1]);
update_rev(ch[r][0]);
rev[r]=0;
}
}
void pushup(int r)
{
maxv[r]=max(max(maxv[ch[r][0]],maxv[ch[r][1]]),key[r]);
sum[r]=sum[ch[r][0]]+sum[ch[r][1]]+key[r];
}
void rotate(int x)
{
int y=pre[x],kind=ch[y][1]==x;
ch[y][kind]=ch[x][!kind];
pre[ch[y][kind]]=y;
pre[x]=pre[y];
pre[y]=x;
ch[x][!kind]=y;
if(rt[y])rt[y]=false,rt[x]=true;
else ch[pre[x]][ch[pre[x]][1]==y]=x;
pushup(y);
}
//将根节点到r的路径上的所有及诶单的标记下方
void P(int r)
{
if(!rt[r])P(pre[r]);
pushdown(r);
}
void Splay(int r)
{
P(r);
while(!rt[r])
{
int f=pre[r],ff=pre[f];
if(rt[f])rotate(r);
else if((ch[ff][1]==f)==(ch[f][1]==r))
rotate(f),rotate(r);
else rotate(r),rotate(r);
}
pushup(r);
}
int Access(int x)
{
int y=0;
for(;x;x=pre[y=x])
{
Splay(x);
rt[ch[x][1]]=true,rt[ch[x][1]=y]=false;
pushup(x);
}
return y;
}
int getroot(int x)
{
Access(x);
Splay(x);
while (ch[x][0])
x = ch[x][0];
return x;
}
//判断是否同根
bool judge(int u,int v)
{
while(pre[u])u=pre[u];
while(pre[v])v=pre[v];
return u==v;
}
//将r变成他所在根
void mroot(int r)
{
Access(r);
Splay(r);
update_rev(r);
}
//调用后u是原来u和v的lca,v和ch[u][1]分别存折lca的两个儿子
void lca(int &u,int &v)
{
Access(v),v=0;
while(u)
{
Splay(u);
if(!pre[u])return;
rt[ch[u][1]]=true;
rt[ch[u][1]=v]=false;
pushup(u);
u=pre[v=u];
}
}
//将u合并到v上
void link(int u,int v)
{
mroot(u);
pre[u]=v;
}
//将v和他的父节点分离
void cut(int v)
{
//mroot(v);
Access(v);
Splay(v);
pre[ch[v][0]]=0;
pre[v]=0;
rt[ch[v][0]]=true;
ch[v][0]=0;
pushup(v);
}
void init()
{
memset(head,-1,sizeof(head));
memset(pre,0,sizeof(pre));
memset(ch,0,sizeof(ch));
memset(rev,0,sizeof(rev));
memset(sum,0,sizeof(sum));
for(int i=0;i<=N;i++)rt[i]=true;
maxv[0]=-INF;
}
void dfs(int u,int f)
{
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(v==f)continue;
pre[v]=u;
dfs(v,u);
}
}
}tree;
int main()
{
int u,v;
char op[10];
while(scanf("%d",&N)!=EOF)
{
tree.init();
for(int i=1;i<N;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
for(int i=1;i<=N;i++)scanf("%d",&tree.key[i]);
tree.dfs(1,0);
scanf("%d",&Q);
while(Q--)
{
scanf("%s%d%d",op,&u,&v);//cout<<op<<endl;
if(op[0]=='C')
{
tree.Access(u);
tree.Splay(u);
tree.key[u]=v;
tree.pushup(u);
}
else if(op[1]=='M')
{
//注意一下三个操作的顺序
tree.mroot(u);
tree.Access(v);
tree.Splay(u);
printf("%d\n",tree.maxv[u]);
}
else
{
tree.mroot(u);
tree.Access(v);
tree.Splay(u);
//tree.Splay(v);
printf("%d\n",tree.sum[u]);
}
}
}
return 0;
}
树链剖分写发(转自 http://www.cnblogs.com/silver-bullet/archive/2013/02/09/2909473.html):
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 30010
#define lson l,m,n<<1
#define rson m+1,r,n<<1|1
using namespace std;
const int inf=1<<30;
struct Edge{
int u,v,next;
Edge(){}
Edge(int _u,int _v,int _next){
u=_u;v=_v;next=_next;
}
}edge[N<<1];
int head[N],cnt;
int sz[N],top[N],fa[N],dep[N],hash[N],w[N],son[N],num;
void init(){
memset(head,-1,sizeof(head));
cnt=num=0;
}
void add(int u,int v){
edge[cnt]=Edge(u,v,head[u]);head[u]=cnt++;
edge[cnt]=Edge(v,u,head[v]);head[v]=cnt++;
}
void dfs(int u,int d){
sz[u]=1;son[u]=0;dep[u]=d;
for(int k=head[u];k!=-1;k=edge[k].next){
int v=edge[k].v;
if(v==fa[u])continue;
fa[v]=u;
dfs(v,d+1);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
}
void build_tree(int u,int pre){
hash[u]=++num;top[u]=pre;
if(son[u])build_tree(son[u],pre);
for(int k=head[u];k!=-1;k=edge[k].next){
int v=edge[k].v;
if(v!=fa[u]&&v!=son[u])build_tree(v,v);
}
}
struct segtree{
int maxn[N<<2],sum[N<<2];
void pushup(int n){
sum[n]=sum[n<<1]+sum[n<<1|1];
maxn[n]=max(maxn[n<<1],maxn[n<<1|1]);
}
void update(int nn,int x,int l,int r,int n){
if(l==r){
maxn[n]=sum[n]=x;
return;
}
int m=(l+r)>>1;
if(nn<=m)update(nn,x,lson);
else update(nn,x,rson);
pushup(n);
}
int query_max(int ll,int rr,int l,int r,int n){
if(ll==l&&rr==r)return maxn[n];
int m=(l+r)>>1;
if(rr<=m)return query_max(ll,rr,lson);
else if(ll>m)return query_max(ll,rr,rson);
else return max(query_max(ll,m,lson),query_max(m+1,rr,rson));
}
int query_sum(int ll,int rr,int l,int r,int n){
if(ll==l&&rr==r)return sum[n];
int m=(l+r)>>1;
if(rr<=m)return query_sum(ll,rr,lson);
else if(ll>m)return query_sum(ll,rr,rson);
else return query_sum(ll,m,lson)+query_sum(m+1,rr,rson);
}
}seg;
int Query_max(int a,int b,int n){
int ta=top[a],tb=top[b],ans=-inf;
while(ta!=tb){
if(dep[ta]<dep[tb]){
swap(ta,tb);swap(a,b);
}
ans=max(ans,seg.query_max(hash[ta],hash[a],1,n,1));
a=fa[ta];ta=top[a];
}
if(dep[a]>dep[b])swap(a,b);
return max(ans,seg.query_max(hash[a],hash[b],1,n,1));
}
int Query_sum(int a,int b,int n){
int ta=top[a],tb=top[b],ans=0;
while(ta!=tb){
if(dep[ta]<dep[tb]){
swap(ta,tb);swap(a,b);
}
ans+=seg.query_sum(hash[ta],hash[a],1,n,1);
a=fa[ta];ta=top[a];
}
if(dep[a]>dep[b])swap(a,b);
return ans+seg.query_sum(hash[a],hash[b],1,n,1);
}
int main(){
int n,a,b,q;
char op[10];
while(~scanf("%d",&n)){
init();
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
add(a,b);
}
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
dfs(1,1);
build_tree(1,1);
for(int i=1;i<=n;i++)seg.update(hash[i],w[i],1,n,1);
scanf("%d",&q);
while(q--){
scanf("%s%d%d",op,&a,&b);
if(strcmp(op,"QMAX")==0){
int ans=Query_max(a,b,n);
printf("%d\n",ans);
}else if(strcmp(op,"QSUM")==0){
int ans=Query_sum(a,b,n);
printf("%d\n",ans);
}else {
seg.update(hash[a],b,1,n,1);
}
}
}
return 0;
}