今天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的数字根情况。经过调整后,问题得到了解决。
541

被折叠的 条评论
为什么被折叠?



