bzoj1036: [ZJOI2008]树的统计Count

树链剖分+线段树(入门)

/**************************************************************
     Problem: 1036
     User: 96655
     Language: C++
     Result: Accepted
     Time:2472 ms
     Memory:5536 kb
****************************************************************/
 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<iostream>
#include<cstdlib>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<stack>
#include<utility>
using namespace std;
const int maxn=30005;
struct Edge
{
     int v,next;
} edge[maxn*2];
int va[maxn],p,head[maxn],clk,n;
void init()
{
     memset (head,-1, sizeof (head));
     p=clk=0;
}
void addedge( int u, int v)
{
     edge[p].v=v;
     edge[p].next=head[u];
     head[u]=p++;
}
int id[maxn],sz[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn];
int xx[maxn];
void dfs1( int u, int f, int d)
{
     dep[u]=d;
     fa[u]=f;
     sz[u]=1;
     son[u]=-1;
     for ( int i=head[u]; ~i; i=edge[i].next)
     {
         int v=edge[i].v;
         if (v==f) continue ;
         dfs1(v,u,d+1);
         sz[u]+=sz[v];
         if (son[u]==-1||sz[v]>sz[son[u]])
             son[u]=v;
     }
}
void dfs2( int u, int tp)
{
     id[u]=++clk;
     xx[id[u]]=va[u];
     top[u]=tp;
     if (son[u]!=-1)dfs2(son[u],tp);
     for ( int i=head[u]; ~i; i=edge[i].next)
     {
         int v=edge[i].v;
         if (v==fa[u]||v==son[u]) continue ;
         dfs2(v,v);
     }
}
int sum[maxn*4],maxv[maxn*4];
void pushup( int rt)
{
     sum[rt]=sum[rt*2]+sum[rt*2+1];
     maxv[rt]=max(maxv[rt*2],maxv[rt*2+1]);
}
void build( int rt, int l, int r)
{
     if (l==r)
     {
         sum[rt]=maxv[rt]=xx[l];
         return ;
     }
     int m=(l+r)>>1;
     build(rt*2,l,m);
     build(rt*2+1,m+1,r);
     pushup(rt);
}
void update( int rt, int l, int r, int pos, int c)
{
     if (l==r)
     {
         maxv[rt]=sum[rt]=c;
         return ;
     }
     int m=(l+r)>>1;
     if (pos<=m)update(rt*2,l,m,pos,c);
     else update(rt*2+1,m+1,r,pos,c);
     pushup(rt);
}
int query1( int rt, int l, int r, int x, int y)
{
     if (x<=l&&r<=y)
     {
         return sum[rt];
     }
     int ans=0;
     int m=(l+r)>>1;
     if (x<=m)ans+=query1(rt*2,l,m,x,y);
     if (y>m)ans+=query1(rt*2+1,m+1,r,x,y);
     return ans;
}
int query2( int rt, int l, int r, int x, int y)
{
     if (x<=l&&r<=y)
     {
         return maxv[rt];
     }
     int ans=-99999;
     int m=(l+r)>>1;
     if (x<=m)ans=max(ans,query2(rt*2,l,m,x,y));
     if (y>m)ans=max(ans,query2(rt*2+1,m+1,r,x,y));
     return ans;
}
int getsum( int u, int v)
{
     int ans=0;
     while (top[u]!=top[v])
     {
         if (dep[top[u]]<dep[top[v]])
             swap(u,v);
         ans+=query1(1,1,n,id[top[u]],id[u]);
         u=fa[top[u]];
     }
     if (dep[u]>dep[v])swap(u,v);
     ans+=query1(1,1,n,id[u],id[v]);
     return ans;
}
int getmax( int u, int v)
{
     int ans=-99999;
     while (top[u]!=top[v])
     {
         if (dep[top[u]]<dep[top[v]])
             swap(u,v);
         ans=max(ans,query2(1,1,n,id[top[u]],id[u]));
         u=fa[top[u]];
     }
     if (dep[u]>dep[v])swap(u,v);
     ans=max(ans,query2(1,1,n,id[u],id[v]));
     return ans;
}
int main()
{
     while (~ scanf ( "%d" ,&n))
     {
         init();
         for ( int i=1; i<n; ++i)
         {
             int u,v;
             scanf ( "%d%d" ,&u,&v);
             addedge(u,v);
             addedge(v,u);
         }
         for ( int i=1; i<=n; i++)
             scanf ( "%d" ,&va[i]);
         dfs1(1,1,0);
         dfs2(1,1);
         build(1,1,n);
         int q;
         scanf ( "%d" ,&q);
         while (q--)
         {
             char s[20];
             int x,y;
             scanf ( "%s%d%d" ,s,&x,&y);
             if (s[0]== 'Q' )
             {
                 if (s[1]== 'M' ) printf ( "%d\n" ,getmax(x,y));
                 else printf ( "%d\n" ,getsum(x,y));
             }
             else update(1,1,n,id[x],y);
         }
     }
     return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值