HDU-4351-线段树

这题纠结了好久。

状态转移,预处理的话要1024*1024*10*10,感觉会T,然后发现 9 当成 0 就变成了 512*512*9*9,于是就这么写了。

然后 T 了好久, 然后各种改 , IO加速。能加 inline 的都加 inline 。 依然 TLE 。

后来找一下别人的代码 , 发现 查询的时候 , 我是在每个查询里面 分别再查询 左右值。别人都放一个结构体里面,这样别人的事logN,我的事logN*logN。

改了之后,WA。

想到不能区分 0 和 9 然后尝试各种区分0和9. 各种wa 。

矩阵看来还是要用1024*1024的。不能这么预处理啊。写一个记忆化的函数吧。然后这个想法又迅速被自己泯灭了。我就是不想这么写。烦死了。

后来死的次数多了,就无所谓了 , 曾经被自己否决的预处理 , 试试吧。预处理 1024*1024*10*10。 625ms。瞬间激动之情难以言表。


每一段存 4 个值。本区间的数根val[0, 9]。前缀可达状态Left[0, 1023] 。后缀可达状态Right[0, 1023]。区间可达状态sum[0, 1023]。

状态转移见代码吧。各种亦或。



const int N = 100002;

__int16 tri_one[1030][10]; //tri_one[a][b] 表示 状态a加上一个数b
__int16 tri_sum[1030][1030]; //表示 a 边和 b 左边一定连着。
__int16 val[N<<2], sum[N<<2], Left[N<<2], Right[N<<2];
__int16 bit[10];
int n;

void init()
{
    for(__int16 i=0; i<=9; i++) bit[i] = 1<<i;
}

void in(__int16 &a)
{
    a = 0;
    char ch;
    while(ch=getchar(), ch<'0'||ch>'9');
    while(ch>='0'&&ch<='9') a = a*10+ch-'0', ch=getchar();
}

void inn(int &a)
{
    a = 0;
    char ch;
    while(ch=getchar(), ch<'0'||ch>'9');
    while(ch>='0'&&ch<='9') a = a*10+ch-'0', ch=getchar();
}

inline __int16 to(__int64 x)
{
    if(x==0) return 0;
    x %= 9;
    return x==0?9:x;
}

void make_one()
{
    __int16 a, b;
    for(a=0; a<1024; a++)
        for(b=0; b<=9; b++)
        {
            __int16 ans = 0;
            for(__int16 i=0; i<=9; i++)
            if( a & bit[i] )
            {
                ans |= bit[ to(i+b) ];
            }
            tri_one[a][b] = ans;
        }
}

void make_sum()
{
    __int16 a, b;
    for(a=0; a<1024; a++)
        for(b=0; b<1024; b++)
        {
            __int16 ans = a|b;
            for(int i=0; i<=9; i++)
            if(a&bit[i])
            {
                for(int j=0; j<=9; j++)
                if(b&bit[j])
                {
                    ans |= bit[ to(i+j) ];
                    if(ans==1023) break;
                }
                if(ans==1023) break;
            }
            tri_sum[a][b] = ans;
        }
}

void build(int l, int r, int k)
{
    if(l==r)
    {
        int x;
        inn(x);
        x = to(x);
        val[k] = x;
        x = 1<<x;
        sum[k] = Left[k] = Right[k] = x;
        return ;
    }
    int mi = (l+r)>>1;
    int lk = k<<1, rk = k<<1|1;
    build(l, mi, lk);
    build(mi+1, r, rk);

    val[k] = to(val[lk]+val[rk]);
    Left[k] = Left[lk]|tri_one[Left[rk]][val[lk]];
    Right[k] = Right[rk]|tri_one[Right[lk]][val[rk]];
    sum[k] = sum[lk]|sum[rk];
    sum[k] |= tri_sum[Right[lk]][Left[rk]];

}

struct node
{
    __int16 val, sum, left, right;
};

void query(int l, int r, int ll, int rr, int k, node &p)
{
    if(l==ll && r==rr)
    {
        p.sum = sum[k];
        p.val = val[k];
        p.left = Left[k];
        p.right = Right[k];
        return;
    }
    int mi = (ll+rr)>>1;
    if(r<=mi) query(l, r, ll, mi, k<<1,  p);
    else if(l>mi) query(l, r, mi+1, rr, k<<1|1, p);
    else
    {
        node lp, rp;
        query(l, mi, ll, mi, k<<1, lp);
        query(mi+1, r, mi+1, rr, k<<1|1, rp);
        p.val = to(lp.val+rp.val);
        p.left = lp.left|tri_one[rp.left][lp.val];
        p.right = rp.right|tri_one[lp.right][rp.val];
        p.sum = lp.sum|rp.sum;
        p.sum |= tri_sum[lp.right][rp.left];
    }
}

int main()
{
    int t, tt=0, n, q, l, r, i, j;
    init();
    make_one();
    make_sum();
    inn(t);
    while(t--)
    {
        inn(n);
        build(1, n, 1);

        inn(q);
        printf("Case #%d:\n", ++tt);
        while(q--)
        {
            inn(l), inn(r);
            node p;
            query(l, r, 1, n, 1, p);
            __int16 res = p.sum;
            i=9, j=1;
            for(; ; i--)
            if( res&bit[i] )
            {
                putchar(i+48);
                i--;
                break;
            }
            for(; i>=0 && j<5; i--)
            if(res & bit[i])
            {
                j++;
                putchar(' ');
                putchar(i+48);
            }
            while(j++ < 5)
            {
                putchar(' ');
                putchar('-');
                putchar('1');
            }
            putchar('\n');
        }
        if(t) putchar('\n');

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值