HDU 5614 Baby Ming and Matrix tree(树链剖分+线段树)

59 篇文章 0 订阅
12 篇文章 0 订阅

Description
规定一个2*2的01矩阵A,能够进行如下2种变换:
旋转变换:做顺时针旋转,把A(i, j)移动到A(j,1- i)
替换:把矩阵A替换成B
给出1棵树,每一个树的节点上,有一个矩阵A​i,铭宝宝喜欢在树上玩矩阵的游戏。每一次在树上选择2个节点a, b,通过上述2种变换,把a到b路径上的矩阵,全部变成B矩阵。
假设旋转变换每次需要2秒,替换每次需要10秒。铭宝宝想知道,每一次操作所需要的最少时间是多少。
Input
输入一个正整数T,表示测试组数
每组数据,输入一个正整数n,表示树有n个节点
输入n-1行,每行2个正整数a, b,表示a, b之间连有一条边
接下去输入2*n行,每行2个数,表示n个2*2的矩阵A​i
输入一个正整数q表示询问组数
每组询问,第一行输入2个正整数a, b
第二、三行输入一个2*2的01矩阵B,表示选择a,b两个点,把a到b路径上的矩阵都变换成矩阵B
(A​i,B的矩阵由2个0和2个1组成)
1≤T≤30,1< n≤20000,1≤q≤20000,1≤a,b≤n
Output
对于每一个Q操作,输出1行,表示变换所需要的最小时间
Sample Input
1
5
3 4
1 2
2 5
1 4
0 0
1 1
0 1
1 0
1 1
0 0
0 1
0 1
1 0
0 1
3
1 5
0 1
1 0
2 3
0 1
0 1
1 2
0 0
1 1
Sample Output
12
22
4
Solution
首先对整棵树树链剖分,由dfs序可以将路径上的操作就变成了若干个区间的操作,用线段树维护每个区间中6种矩阵的数量,打表搞出6种矩阵之间转换的最小代价矩阵,然后累加代价即可
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
//树链剖分,解决树上路径问题,时间复杂度log n*log n
#define maxn 22222
int cost[6][6]={{0,2,4,6,10,10},{6,0,2,4,10,10},{4,6,0,2,10,10},{2,4,6,0,10,10},{10,10,10,10,0,2},{10,10,10,10,2,0}};
int judge(int a,int b,int c,int d)
{
    if(a+b==2)return 0;
    if(b+c==2)return 1;
    if(c+d==2)return 2;
    if(d+a==2)return 3;
    if(a+c==2)return 4;
    if(b+d==2)return 5;
}
struct edge
{
    int to,next;
}E[2*maxn];
int head[maxn],tot;
int idx;//时间戳,用于得到dfs序 
int size[maxn];//size[i]表示以i节点为根节点的子树中节点个数 
int fa[maxn];// fa[i]表示i节点的父亲节点 
int son[maxn];//son[i]表示i节点的重儿子节点(如果没有重儿子则son[i]=i) 
int dep[maxn];//dep[i]表示i节点在树中的深度 
int top[maxn];//top[i]表示i节点所处重链中深度最低的节点 
int id[maxn];//id[i]表示i节点的dfs序 
int pos[maxn];//pos[i]表示dfs序为i的节点 
int l[maxn];//l[i]表示i节点所在子树(dfs序连续)最小的dfs序,可以作为这棵子树在线段树中的左端点 
int r[maxn]; //r[i]表示i节点所在子树(dfs序连续)最大的dfs序,可以作为这棵子树在线段树中的右端点
int v[maxn];//v[i]表示dfs序列中每个点的值(即矩阵种类) 
void init()
{
    memset(head,-1,sizeof(head));
    idx=tot=0;
    dep[1]=fa[1]=size[0]=0;//默认1节点为根节点,初始化其父亲节点为0,深度为0 
    memset(son,0,sizeof(son));
}
void add(int u,int v)
{
    E[tot].to=v;
    E[tot].next=head[u];
    head[u]=tot++;
} 
void dfs1(int u)//得到size,fa,dep,son
{
    size[u]=1;
    for(int i=head[u];~i;i=E[i].next)
    {
        int v=E[i].to;
        if(v!=fa[u]) 
        {
            fa[v]=u;//v的父亲节点是u 
            dep[v]=dep[u]+1;//儿子的深度等于父亲的深度加一 
            dfs1(v);//深搜 
            size[u]+=size[v];//父亲节点为根节点的子树节点个数等于各儿子节点为根节点的子树节点个数之和加一(父亲节点本身) 
            if(size[son[u]]<size[v]) son[u]=v;//更新重儿子 
        }
    }
}
void dfs2(int u,int topu)//得到top,id,pos,l,r 
{
    top[u]=topu;
    id[u]=++idx;//得到u节点的dfs序 
    pos[idx]=u;//记录这个dfs序对应的节点 
    l[u]=idx;//记录以u节点为根的子树最小的dfs序
    if(son[u]) dfs2(son[u],top[u]);//有重儿子首先深搜重儿子 
    for(int i=head[u];~i;i=E[i].next)
    {
        int v=E[i].to;
        if(v!=fa[u]&&v!=son[u]) dfs2(v,v);//深搜所有儿子节点 
    }
    r[u]=idx;//记录以u节点为根的子树最大的dfs序
}
struct Tree
{
    int left,right,sum[7],flag;
}T[4*maxn];
void push_up(int t)
{
    for(int i=0;i<6;i++)
        T[t].sum[i]=T[2*t].sum[i]+T[2*t+1].sum[i]; 
}
int query(int l,int r,int t,int v);
void push_down(int t)
{
    if(T[t].flag!=-1)
    {
        int mid=(T[t].left+T[t].right)>>1;
        query(T[t].left,mid,2*t,T[t].flag);
        query(mid+1,T[t].right,2*t+1,T[t].flag);
        T[t].flag=-1;       
    }
}
void build(int l,int r,int t)
{
    T[t].left=l;
    T[t].right=r;
    T[t].flag=-1;
    for(int i=0;i<6;i++)T[t].sum[i]=0;
    if(l==r)
    {
        T[t].sum[v[l]]=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,2*t);
    build(mid+1,r,2*t+1);
    push_up(t);
}
int query(int l,int r,int t,int v)
{
    int ans=0;
    if(T[t].left==l&&T[t].right==r)
    {
        for(int i=0;i<6;i++)
        {
            ans+=cost[i][v]*T[t].sum[i];
            T[t].sum[i]=0;
        }
        T[t].sum[v]=T[t].right-T[t].left+1;
        T[t].flag=v;
        return ans;
    }
    push_down(t);
    if(r<=T[2*t].right) ans=query(l,r,2*t,v);
    else if(l>=T[2*t+1].left) ans=query(l,r,2*t+1,v);
    else ans=query(l,T[2*t].right,2*t,v)+query(T[2*t+1].left,r,2*t+1,v);
    push_up(t);
    return ans;
}
int Query(int u,int v,int x)
{
    int top1=top[u],top2=top[v],ans=0;
    while(top1!=top2)
    {
        if(dep[top1]<dep[top2])
        {
            swap(top1,top2);
            swap(u,v);
        }
        ans+=query(id[top1],id[u],1,x);
        u=fa[top1];
        top1=top[u];
    }
    if(dep[u]>dep[v]) swap(u,v);
    ans+=query(id[u],id[v],1,x);
    return ans;
}
int Case,n,q;
int main()
{
    scanf("%d",&Case);
    while(Case--)
    {
        scanf("%d",&n);
        init();
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v),add(v,u);
        }
        dfs1(1);
        dfs2(1,1);
        for(int i=1;i<=n;i++)
        {
            int a,b,c,d,temp;
            scanf("%d%d%d%d",&a,&b,&d,&c);
            temp=judge(a,b,c,d);
            v[id[i]]=temp; 
        }
        build(1,n,1);
        scanf("%d",&q);
        while(q--)
        {
            int u,v,a,b,c,d;
            scanf("%d%d%d%d%d%d",&u,&v,&a,&b,&d,&c);
            int x=judge(a,b,c,d);
            int ans=Query(u,v,x);
            printf("%d\n",ans);
        }
    }
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值