hdu 4351 区间合并 树根

今天T了一题,WA了一题,肿么办呢?

下面是WA的那题,按照题解写的。


12/08/13

过了几天,我知道了要用一个东西去记录树由于中间m分开的两个子区间的前缀、后缀、和,并且注意了0的数字根是0的情况,

又改了一改。

T了。

/*
Pro: 0

Sol:

date:
*/
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define maxn 222222
#define lson l,m,rt << 1
#define ls  rt << 1
#define rs rt << 1 | 1
#define rson m + 1, r, rt << 1 | 1
#define havem int m = (l + r ) >> 1
//每个线段树的节点维护三个东西,子区间和,前缀(即从左到右连续相加的和,如1,1+2,1+2+3)
//还有后缀。
short pre[maxn << 2],suf[maxn << 2],sum[maxn << 2],ans;
int t,n,m,tmp,ka;
inline short getrt(int num){
    return ((num - 1) % 9 + 1);
}
void push_up(int rt){
//维护根的子区间和
    sum[rt] = getrt(sum[ls] + sum[rs]);
//维护根的pre
    pre[rt] |= pre[ls];     //根的前缀等于左孩子的所有前缀
    pre[rt] |= (1 << (sum[rt])); //根的最大前缀还等于自己子区间的和
    //根的前缀还等于根的左孩子的区间和加上右孩子的前缀
    int x = getrt(sum[ls]) ;
    for(int i = 9; i >= 0; i --){
        if(pre[rs] & (1 << i))
            pre[rt] |= (1 << (getrt( i + x )) );
    }
//维护根的suf
    suf[rt] |= suf[rs]; //根的后缀等于右孩子的所有后缀
    suf[rt] |= (1 << (sum[rt]));//根的最大后缀还等于自己子区间的和
    //根的后缀还等于根的右孩子的区间和加上左孩子的后缀
    x = getrt(sum[rs]);
    for(int i = 9; i >= 0; i --){
        if(suf[ls] & (1 << i))
            suf[rt] = suf[rt] | ( 1 << (getrt(i + x)));
    }
}

int in(){
    int flag = 1;
    char ch;
    int a = 0;
    while((ch = getchar()) == ' ' || ch == '\n');
    if(ch == '-') flag = -1;
    else
        a += ch - '0';
    while((ch = getchar()) != ' ' && ch != '\n'){
        a *= 10;
        a += ch - '0';
    }
    return flag * a;
}
void out(int a){
    if(a < 0){
        putchar('-');
        a = -a;
    }
    if(a >= 10)out(a / 10);
    putchar(a % 10 + '0');
}
void build(int l, int r, int rt){
    if(l == r){
        tmp = in(); tmp = getrt(tmp);
        sum[rt] = tmp;
        pre[rt] = suf[rt] = (1 << tmp);
        return ;
    }havem;
    build(lson);    build(rson);
    push_up(rt);
}
struct Node{
    short sum,pre,suf,ans;
    Node(){
        sum = pre = suf = ans = 0;
    }
};
void query(int L,int R,int l, int r, int rt, Node &node){
    if(L <= l && r <= R){
        if(node.ans >= ka)   return ;
        node.pre |= pre[rt]; node.suf |= suf[rt]; node.sum = sum[rt];
//        if(l == r) return ;

        for(int i = 9; i >=0 ; i --){//起终点不是在区间端点的
            for(int o = 9; o >= 0; o --){
                if( (suf[ls] & (1 << i) )&& (pre[rs] & (1 << o))){
                    node.ans |= (1 << getrt(i + o));
                }else if(suf[ls] & (1 << i)){//IMPORTANT
                    node.ans |= (1 << i);
                }else if(pre[rs] & (1 << o)){
                    node.ans |= (1 << o);
                }
            }
        }   node.ans |= node.pre;   node.ans |= node.suf;

        return ;
    }havem;
    if(node.ans >= ka)   return ;
    if(R <= m) query(L,R,lson,node);
    else if(L >= m + 1) query(L,R,rson,node);
    else{//要查询的区间包含m的情况
        Node lls ,rrs;
        query(L,m,lson,lls);
        query(m + 1, R, rson,rrs);

        node.sum = getrt(lls.sum + rrs.sum);

        node.pre |= lls.pre;    node.pre |= (1 << (node.sum));
        for(int i = 9; i >= 1;i --)
            if( rrs.pre & (1 << i) )
                node.pre |= (1 << (getrt(lls.sum + i) ));

        node.suf |= rrs.suf;    node.suf |= (1 << (node.sum));
        for(int i = 9; i >= 1;i --)
            if(lls.suf & (1 << i))
                node.suf |= (1 << (getrt(rrs.sum + i)  ) );

//处理完了前后缀的问题,来处理答案。
        for(int i = 9; i >=0 ; i --){
            for(int o = 9; o >= 0; o --){
                if( (rrs.pre & (1 << i) )&& (lls.suf & (1 << o))){
                    node.ans |= (1 << getrt(i + o));
                }else if(rrs.pre & (1 << i)){
                    node.ans |= (1 << i);
                }else if(lls.suf & (1 << o)){
                    node.ans |= (1 << o);
                }
            }
        }
        node.ans |= node.pre;   node.ans |= node.suf;
        node.ans |= lls.ans;   node.ans |= rrs.ans;
        return ;
    }
}
int main(){
    scanf("%d",&t); ka = 0;
    for(int i = 9; i >= 5; i --)
        ka |= (1 << i);
    for(int ca = 1; ca <= t; ca ++){
        printf("Case #%d:\n",ca);
        scanf("%d",&n);     build(1,n,1);   //cout << "yes" << endl;
        scanf("%d",&m);
        int a,b;
        for(int j = 0; j < m; j ++){
            a = in(); b = in(); Node p;
            query(a,b,1,n,1,p);
            int Num = 0;   int tp[10];  memset(tp,0,sizeof(tp));
            for(int i = 9; i >= 0; i --){
                if(p.ans & (1 << i)) tp[Num ++] = i;
            }
            if(Num >= 5){//
                for(int i = 0; i < 4; i ++) {out(tp[i]); putchar(' ');}
                    out(tp[4]);
                    putchar('\n');
            }
            else{
                for(int i = 0; i < Num; i ++){ out(tp[i]); putchar(' ');
                }
                for(int i = Num ;i < 4; i ++){ out(-1); putchar(' ');
                }
                out(-1);    putchar('\n');
            }
        }
    }
    return 0;
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值