http://acm.hdu.edu.cn/showproblem.php?pid=4351
线段树 每个节点保存前缀 后缀 和 剩余情况 中(k)(0<=k<=9) 是否出现
除了叶子节点外 其它节点要用左右孩子 来维护
求答案时类似
维护的过程中有重复的计算 需要用打表和位运算来优化 否则超时
注意0的情况
代码及其注释:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<stack> #include<cmath> #define LL long long using namespace std; const int N=100005; struct node { int l,r,root;//root 为这一段总起来的 结果 int lt,rt,mt;//前缀 后缀 和其它情况 中k(0--9)是否出现 用二进制表示 }mem[N*4]; struct tt//同上 { int lt,rt,mt,root; }ans[N*4]; int mul[1024][1024];//将 i 和 j 两种情况合并 int a[N]; inline int Froot(int x)//只能处理小于等于18的情况 { if(x>9) return x-9; return x; } void begin()//打表 来优化时间 否则超时 { for(int x=1;x<1024;++x) for(int y=1;y<1024;++y) { mul[x][y]=0; for(int i=0;i<=9;++i) { if(x&(1<<i)) for(int j=0;j<=9;++j) if(y&(1<<j)) mul[x][y]=(mul[x][y]|(1<<Froot(i+j))); } } } void updatemem(int x)//维护x 的前缀 后缀 等信息 { mem[x].root=mul[ mem[ x<<1 ].root ][ mem[ x<<1|1 ].root ];//root 是左右子树 root 的 更新 mem[x].rt=(mem[ x<<1|1 ].rt | (mul[ mem[ x<<1 ].rt ][ mem[ x<<1|1 ].root]));//后缀由 右子树后缀 左子树后缀+右子树全部来更新 mem[x].lt=(mem[ x<<1 ].lt | (mul[ mem[ x<<1|1 ].lt ][ mem[ x<<1 ].root ]));//前缀由 左子树前缀 右子树前缀+左子树全部来更新 mem[x].mt=(mem[ x<<1 ].mt | mem[ x<<1|1 ].mt | (mul[ mem[ x<<1 ].rt ][ mem[ x<<1|1 ].lt ]));//其它 由 左子树其它 右子树其它 左子树后缀+右子树前缀更新 } void updateans(int x)//同上 { ans[x].root=mul[ ans[ x<<1 ].root ][ ans[ x<<1|1 ].root ]; ans[x].rt=(ans[ x<<1|1 ].rt | (mul[ ans[ x<<1 ].rt ][ ans[ x<<1|1 ].root])); ans[x].lt=(ans[ x<<1 ].lt | (mul[ ans[ x<<1|1 ].lt ][ ans[ x<<1 ].root ])); ans[x].mt=(ans[ x<<1 ].mt | ans[ x<<1|1 ].mt | (mul[ ans[ x<<1 ].rt ][ ans[ x<<1|1 ].lt ])); } void build(int x,int l,int r)//建树 { mem[x].l=l; mem[x].r=r; if(l==r) { mem[x].lt=1<<a[l];//初始化 mem[x].rt=1<<a[l]; mem[x].mt=1<<a[l]; mem[x].root=1<<a[l]; return ; } int mid=(l+r)>>1; build(x<<1,l,mid); build(x<<1|1,mid+1,r); updatemem(x);//更新 } void Fans(int x,int l,int r) { if(mem[x].l==l&&mem[x].r==r) { ans[x].lt=mem[x].lt;//答案搜索边界 ans[x].rt=mem[x].rt; ans[x].mt=mem[x].mt; ans[x].root=mem[x].root; return ; } int mid=(mem[x].l+mem[x].r)>>1; if(mid<l) { Fans(x<<1|1,l,r);//只在右子树 传结果上来 ans[x].lt=ans[x<<1|1].lt; ans[x].rt=ans[x<<1|1].rt; ans[x].mt=ans[x<<1|1].mt; ans[x].root=ans[x<<1|1].root; }else if(mid>=r) { Fans(x<<1,l,r); ans[x].lt=ans[x<<1].lt; ans[x].rt=ans[x<<1].rt; ans[x].mt=ans[x<<1].mt; ans[x].root=ans[x<<1].root; }else { Fans(x<<1,l,mid); Fans(x<<1|1,mid+1,r); updateans(x);//左右都有 更新 } } int main() { //freopen("data.txt","r",stdin); begin(); int T; scanf("%d",&T); for(int c=1;c<=T;++c) { int n; scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); if(a[i]<=18) a[i]=Froot(a[i]); else//注意很大的情况 { a[i]=a[i]%9; if(a[i]==0) a[i]=9; } } build(1,1,n); int q; scanf("%d",&q); printf("Case #%d:\n",c); while(q--) { int l,r; scanf("%d %d",&l,&r); Fans(1,l,r); int a=(ans[1].lt|ans[1].rt|ans[1].mt); int k=0; for(int i=9;k<5&&i>=0;--i) { if(a&(1<<i)) { printf("%d",i); ++k; if(k<5) printf(" "); } } while(k<5) { printf("-1"); ++k; if(k<5) printf(" "); } printf("\n"); } if(c<T) printf("\n"); } return 0; }