JZOJ.5289【NOIP2017模拟8.17】偷笑
题意就是区间修改和区间查询,其中查询是某区间内不含2,3以外的数的个数。
区间修改我们可以用线段树来维护,但问题就在查询。线段树查询并不支持直接查询题目要求的数的个数,我们虽然可以一个一个检查,但除法取模运算相比也比较慢,每次查询的nlogn复杂度也接受不了。
直接维护不好做,我们可以尝试改变一下线段树维护的信息。
我们注意到题目给出的a[i]的最大值不会超过20000,在20000内符合题目的233数一共也只有31个,我们可以预处理这些,然后维护每个数a[i]与这些数相差最小的且比a[i]大的数的差值,很明显,当差值为0的时候就说明它是个233数,如果差值小于0,我们就要找下一个比它大且相差最小的233数,作差比较,由于每个数最多被修改31次,所以复杂度为O(31mlogn)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<ctime> 5 #define N 1600000 6 using namespace std; 7 struct data{ 8 int cnt,min,mark; 9 }tree[N]; 10 int f[32]={2,3,22,23,32,33,222,223,232,233,322,323,332,333,2222,2223,2232,2233,2322,2323,2332,2333,3222,3223,3232,3233,3322,3323,3332,3333,22222}; 11 int n,m,voi[N/4],x,y,z,top[N/4]; 12 char str[10]; 13 void build(int root,int l,int r){ 14 tree[root].mark=0; 15 if (l==r){ 16 while (f[top[l]]<voi[l]) top[l]++; 17 tree[root].min=f[top[l]]-voi[l]; 18 if (tree[root].min==0) tree[root].cnt=1; 19 return; 20 } 21 int mid=(l+r)>>1; 22 build(root<<1,l,mid); 23 build(root<<1|1,mid+1,r); 24 tree[root].min=min(tree[root<<1].min,tree[root<<1|1].min); 25 tree[root].cnt=tree[root<<1].cnt+tree[root<<1|1].cnt; 26 } 27 void pushdown(int root,int l,int r){ 28 if (tree[root].mark){ 29 tree[root<<1].mark+=tree[root].mark; 30 tree[root<<1|1].mark+=tree[root].mark; 31 tree[root<<1].min+=tree[root].mark; 32 tree[root<<1|1].min+=tree[root].mark; 33 tree[root].mark=0; 34 } 35 return; 36 } 37 void updata(int root,int l,int r){ 38 if (tree[root].min>0) return; 39 if (l==r){ 40 if (tree[root].min<0){ 41 voi[l]-=tree[root].mark; 42 tree[root].mark=0; 43 while (f[top[l]]<voi[l]) top[l]++; 44 tree[root].min=f[top[l]]-voi[l]; 45 } 46 if (tree[root].min==0) tree[root].cnt=1; 47 else tree[root].cnt=0; 48 return; 49 } 50 pushdown(root,l,r); 51 int mid=(l+r)>>1; 52 updata(root<<1,l,mid); 53 updata(root<<1|1,mid+1,r); 54 tree[root].min=min(tree[root<<1].min,tree[root<<1|1].min); 55 tree[root].cnt=tree[root<<1].cnt+tree[root<<1|1].cnt; 56 } 57 void add(int root,int l,int r,int x,int y,int z){ 58 if ((x<=l)&&(y>=r)){ 59 tree[root].min+=z; 60 tree[root].mark+=z; 61 if (tree[root].min<=0) 62 updata(root,l,r); 63 return; 64 } 65 pushdown(root,l,r); 66 int mid=(l+r)>>1; 67 if (x<=mid) add(root<<1,l,mid,x,y,z); 68 if (y>mid) add(root<<1|1,mid+1,r,x,y,z); 69 tree[root].min=min(tree[root<<1].min,tree[root<<1|1].min); 70 tree[root].cnt=tree[root<<1].cnt+tree[root<<1|1].cnt; 71 } 72 int count(int root,int l,int r,int x,int y){ 73 if ((x<=l)&&(y>=r)) return tree[root].cnt; 74 int ans=0; 75 int mid=(l+r)>>1; 76 if (x<=mid) ans+=count(root<<1,l,mid,x,y); 77 if (y>mid) ans+=count(root<<1|1,mid+1,r,x,y); 78 return ans; 79 } 80 int main(){ 81 scanf("%d%d",&n,&m); 82 for (int i=1;i<=n;i++) 83 scanf("%d",&voi[i]); 84 build(1,1,n); 85 while (m--){ 86 scanf("%s",str); 87 if (str[0]=='c'){ 88 scanf("%d%d",&x,&y); 89 printf("%d\n",count(1,1,n,x,y)); 90 } 91 else{ 92 scanf("%d%d%d",&x,&y,&z); 93 add(1,1,n,x,y,-z); 94 } 95 } 96 return 0; 97 }
(cin只读入字符串还是巨慢QAQ改成scanf瞬间从3000ms+变成300ms+)