洛谷P4891 序列 || 膜法阵,魔法阵

https://www.luogu.org/problemnew/show/P4891

一道几乎一样的题http://210.33.19.103/contest/1130/problem/3 题面https://files.cnblogs.com/files/hehe54321/2019%E7%9C%81%E9%80%89%E8%81%94%E5%90%88%E8%AE%AD%E7%BB%8310.pdf.zip

(这题看了题解后做的)题解(这个站看上去有点像那种爬虫站,并没有在原博客上找到,可能原博客被删掉了?)

首先,设C中关键点为C[i]!=C[i-1]的i(1也是关键点)

整个C序列被这些关键点划分成了一些段,段内部C值都是相等的

每一次修改A都越改越大,因此会删掉一些关键点,然后加入O(1)个关键点(大概理解一下。。);那么,总共加入关键点的数量是O(n+q)级别的,总共删除关键点的数量也是O(n+q)级别的

设一个势能函数,函数的值是序列中有多少个位置的C<B

修改A的时候,由于任何位置的C都不会越改越小,势能函数会减少这样一个值:这一次修改过程中有多少个位置由C<B变为C>=B

每一次单点修改B,最多使得势能函数增加1

因此,可以设计一个算法,使得修改A的时候,复杂度与“这一次修改过程中有多少个位置由C<B变为C>=B”有关,这样复杂度就是对的

每一次修改A的时候,相当于对于C做一些区间修改(根据上面的结论,所有操作的区间修改总次数是O(n+q)的;要找到这个修改的区间的话,可以用另外一个数据结构维护;貌似为了减小代码复杂度,这个也可以直接在下面线段树的基础上实现,就每个节点多记一个区间内A的最大值ma)

每次区间修改,大概可以这样用线段树维护一下:

线段树每个节点存:

区间内所有C<B的位置的C的积a1;区间内C<B的位置的个数n1;

区间内所有C>=B的位置的B的积a2;区间内C>=B的位置的个数n2;

区间内所有C<B的位置的B的最小值mi(没有则设为INF);

区间修改的懒标记;

这样的话,区间修改C的时候,dfs线段树(设要将区间[l,r]内的C都改为y);首先,只走与[l,r]交集不为空的区间;走到不被[l,r]包含的区间的时候直接递归下去,回溯的时候更新节点信息;走到被[l,r]包含的区间的时候,当且仅当它的mi<=y时,说明区间内存在点由C<B变为C>=B,继续递归下去,回溯的时候更新节点信息;否则打个懒标记就走,不要递归下去

这样的复杂度就是$O((n+q)log^2n)$(第二个log是由于区间修改打懒标记以及pushdown的时候更新a1需要快速幂)

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<vector>
  5 #include<set>
  6 using namespace std;
  7 #define fi first
  8 #define se second
  9 #define mp make_pair
 10 #define pb push_back
 11 typedef long long ll;
 12 typedef unsigned long long ull;
 13 const int md=1000000007;
 14 int poww(int a,int b)
 15 {
 16     int ans=1;
 17     for(;b;b>>=1,a=ull(a)*a%md)
 18         if(b&1)
 19             ans=ull(ans)*a%md;
 20     return ans;
 21 }
 22 int a[100011],b[100011],c[100011];
 23 int n,qq;
 24 namespace S
 25 {
 26     struct Nd
 27     {
 28         int a1,n1;
 29         int a2,n2;
 30         int mi,ma;
 31         int tag;//-1表示没有lazytag
 32     }d[400011];
 33 #define LC (u<<1)
 34 #define RC (u<<1|1)
 35     void doSet(int u,int y)
 36     {
 37         d[u].tag=y;
 38         d[u].a1=poww(y,d[u].n1);
 39     }
 40     void pd(int u)
 41     {
 42         if(d[u].tag!=-1)
 43         {
 44             doSet(LC,d[u].tag);
 45             doSet(RC,d[u].tag);
 46             d[u].tag=-1;
 47         }
 48     }
 49     void upd(int u)
 50     {
 51         d[u].a1=ull(d[LC].a1)*d[RC].a1%md;
 52         d[u].n1=d[LC].n1+d[RC].n1;
 53         d[u].a2=ull(d[LC].a2)*d[RC].a2%md;
 54         d[u].n2=d[LC].n2+d[RC].n2;
 55         d[u].mi=min(d[LC].mi,d[RC].mi);
 56         d[u].ma=max(d[LC].ma,d[RC].ma);
 57     }
 58     void build(int l,int r,int u)
 59     {
 60         d[u].tag=-1;
 61         if(l==r)
 62         {
 63             if(c[l]<b[l])
 64             {
 65                 d[u].a1=c[l];d[u].n1=1;
 66                 d[u].a2=1;
 67                 d[u].mi=b[l];
 68             }
 69             else
 70             {
 71                 d[u].a1=1;
 72                 d[u].a2=b[l];d[u].n2=1;
 73                 d[u].mi=0x3f3f3f3f;
 74             }
 75             d[u].ma=a[l];
 76             return;
 77         }
 78         int mid=(l+r)>>1;
 79         build(l,mid,LC);build(mid+1,r,RC);
 80         upd(u);
 81     }
 82     void changea(int L,int x,int l,int r,int u)
 83     {
 84         if(l==r)    {d[u].ma=x;return;}
 85         pd(u);
 86         int mid=(l+r)>>1;
 87         if(L<=mid)    changea(L,x,l,mid,LC);
 88         else    changea(L,x,mid+1,r,RC);
 89         upd(u);
 90     }
 91     int find1(int x,int l,int r,int u)//找到最靠左的位置使得C值>x,不存在则返回r+1
 92     {
 93         if(d[u].ma<=x)    return r+1;
 94         int pa=-1;
 95         while(l!=r)
 96         {
 97             int mid=(l+r)>>1;
 98             if(max(d[LC].ma,pa)>x)    r=mid,u=LC;
 99             else    pa=max(pa,d[LC].ma),l=mid+1,u=RC;
100         }
101         return l;
102     }
103     int queryc0(int L,int l,int r,int u)//返回用ma计算出的C值
104     {
105         int ans=-1;
106         while(l!=r)
107         {
108             int mid=(l+r)>>1;
109             if(L<=mid)    r=mid,u=LC;
110             else    ans=max(ans,d[LC].ma),l=mid+1,u=RC;
111         }
112         return max(ans,d[u].ma);
113     }
114     void changec(int L,int R,int x,int l,int r,int u)
115     {
116         if(r<L || R<l)    return;
117         if(L<=l && r<=R && d[u].mi>x)
118         {
119             doSet(u,x);
120             return;
121         }
122         if(l==r)
123         {
124             d[u].a1=1;d[u].n1=0;
125             d[u].a2=b[l];d[u].n2=1;
126             d[u].mi=0x3f3f3f3f;
127             return;
128         }
129         pd(u);
130         int mid=(l+r)>>1;
131         changec(L,R,x,l,mid,LC);
132         changec(L,R,x,mid+1,r,RC);
133         upd(u);
134     }
135     void changeb(int L,int x,int c,int l,int r,int u)
136     {
137         if(l==r)
138         {
139             c=max(c,d[u].ma);
140             if(c<x)
141             {
142                 d[u].a1=c;d[u].n1=1;
143                 d[u].a2=1;d[u].n2=0;
144                 d[u].mi=x;
145             }
146             else
147             {
148                 d[u].a1=1;d[u].n1=0;
149                 d[u].a2=x;d[u].n2=1;
150                 d[u].mi=0x3f3f3f3f;
151             }
152             return;
153         }
154         pd(u);
155         int mid=(l+r)>>1;
156         if(L<=mid)    changeb(L,x,c,l,mid,LC);
157         else    changeb(L,x,max(c,d[LC].ma),mid+1,r,RC);
158         upd(u);
159     }
160 }
161 int main()
162 {
163     int i,j,idx,x,y,t;
164     scanf("%d%d",&n,&qq);
165     for(i=1;i<=n;++i)
166     {
167         scanf("%d",a+i);
168         c[i]=i==1?a[i]:max(a[i],c[i-1]);
169     }
170     for(i=1;i<=n;++i)
171         scanf("%d",b+i);
172     S::build(1,n,1);
173     while(qq--)
174     {
175         scanf("%d%d%d",&idx,&x,&y);
176         if(idx==0)
177         {
178             t=S::queryc0(x,1,n,1);
179             S::changea(x,y,1,n,1);
180             if(y<=t)    goto xx1;
181             t=S::find1(y,1,n,1)-1;
182             S::changec(x,t,y,1,n,1);
183         }
184         else
185         {
186             b[x]=y;
187             S::changeb(x,y,-1,1,n,1);
188         }
189 xx1:;
190         printf("%llu\n",ull(S::d[1].a1)*S::d[1].a2%md);
191     }
192     return 0;
193 }
View Code

原题解的备份,来自https://www.twblogs.net/a/5baa45a32b717750855c8b75/zh-cn,博客https://www.cnblogs.com/flashhu/

洛谷题目传送门

闲话

考场上一眼看出这是个毒瘤线段树准备杠题,发现实在太难调了,被各路神犇虐哭qwq

考后看到各种优雅的暴力AC。。。。。。宝宝心里苦qwq

思路分析

题面里面是一堆乱七八糟的限制和性质,这时候需要冷静想想有没有可利用的地方。蒟蒻一开始往势能线段树上面想了想。

定义一个全局势能函数,为所有\(C_i<B_i\)的位置个数。注意两个操作的修改都不会小于原来的数。

一个是改\(A\),相当于对\(C\)进行区间设置,此时我们每暴力找到一个原来\(C_i<B_i\)但是现在\(C_i\ge y\)的位置,就需要在线段树内跳\(\log\)层,再修改,势能函数就会下降。如果碰到那些修改以后对势能没有影响的子区间,就跳过而不继续暴力递归下去。

一个是改\(B\),是单点修改,线段树内跳\(\log\)层,势能函数至多加\(1\)。

于是,如果我们能够维护信息,从而判断和控制哪里该暴力递归、哪里该跳过的话,我们总的在线段树内跳的次数不会超过\((n+q)\log n\)。

下面用a代替了\(C\),b代替了\(B\)。蒟蒻在线段树里维护了:

  • pa:区间所有a<b的位置的a的积
  • pb:区间所有a>=b的位置的b的积
  • ma:区间a的最大值
  • mb:区间所有a<b的位置的b的最小值,没有则设成INF
  • cnt:区间所有a<b的位置的总数
  • la:bool型变量,区间设置懒标记

修改a的时候,利用单调不降的性质,我们在线段树上先通过二分来对需要修改的若干个子树进行定位。对于当前子区间,如果当前设置值\(y\)比mb要小,那么设置对答案没有影响,直接打上区间设置标记后退出;否则继续递归直到找到叶子节点,进行修改后退出。

修改b就比较轻松,只要找到对应的叶子节点改完后一路回溯即可。

很多事都是说起来容易做起来难,这题也不例外。调试几乎花了整个晚上。要注意的细节很多,也只好自己仔细思考了。

时间复杂度\(O(n\log n+q\log^2n)\),上界很松。多出来的\(\log\)是区间改a时需要快速幂更新pa造成的。

跑了不到200ms,比什么树套树、分块还是要好看一点,但是被暴力碾压也是有点无奈啊~

 1 #include<bits/stdc++.h>
 2 #define RG register
 3 #define R RG int
 4 #define I inline
 5 #define G if(++ip==ie)fread(ip=buf,1,N,stdin)
 6 using namespace std;
 7 typedef long long LL;
 8 const LL N=1<<18,YL=1e9+7;
 9 char buf[N],*ie=buf+N,*ip=ie-1;
10 int y,a[N];
11 I int in(){
12     G;while(*ip<'-')G;
13     R x=*ip&15;G;
14     while(*ip>'-'){x*=10;x+=*ip&15;G;}
15     return x;
16 }
17 I LL qpow(RG LL b,R k){//快速幂
18     RG LL a=1;
19     for(;k;k>>=1,(b*=b)%=YL)
20         if(k&1)(a*=b)%=YL;
21     return a;
22 }
23 struct Node{//个人认为写指针比较直观(貌似immortalCO也有这样的看法)
24     Node*lc,*rc;bool la;
25     LL pa,pb;int l,r,m,ma,mb,cnt;
26     I void up(){//上传
27         pa=lc->pa*rc->pa%YL;
28         pb=lc->pb*rc->pb%YL;
29         ma=max(lc->ma,rc->ma);
30         mb=min(lc->mb,rc->mb);
31         cnt=lc->cnt+rc->cnt;
32     }
33     I void dn(){//下传区间设置标记
34         if(la){
35             lc->ma=rc->ma=ma;
36             lc->la=rc->la=1;la=0;
37             lc->pa=qpow(ma,lc->cnt);
38             rc->pa=qpow(ma,rc->cnt);
39         }
40     }
41     I void build(R s,R e){//建树
42         l=s;r=e;m=(s+e)>>1;la=0;
43         if(s==e){
44             ma=a[l];mb=in();
45             (cnt=ma<mb)?(pa=ma,pb=1):(pa=1,pb=mb,mb=YL);
46             return;
47         }
48         (lc=new Node)->build(l,m);
49         (rc=new Node)->build(m+1,r);
50         this->up();
51     }
52     I void upda(){//区间修改a
53         if(y<mb){//对区间势能没有影响
54             pa=qpow(ma=y,cnt);la=1;
55             return;
56         }
57         if(l==r){//到了叶子节点
58             ma=y;pa=1;pb=mb;mb=YL;cnt=0;
59             return;
60         }
61         this->dn();
62         lc->upda();rc->upda();
63         this->up();
64     }
65     I void updb(R s){//单点更新b
66         if(l==r){//仔细判断三种情况再修改
67             if(ma<pb)mb=y;
68             else if(ma<y)pa=ma,pb=cnt=1,mb=y;
69             else pb=y;
70             return;
71         }
72         this->dn();
73         (s<=m?lc:rc)->updb(s);
74         this->up();
75     }
76     I void bound(R s){//线段树二分定位,注意细节
77         if(s==l&&ma<y)return this->upda();
78         if(l==r)return;
79         this->dn();
80         if(s>m)return rc->bound(s),this->up();
81         if(lc->ma<y)rc->bound(m+1);
82         lc->bound(s);
83         this->up();
84     }
85 };
86 int main(){
87     R n=in(),q=in(),op,x;
88     for(R i=1;i<=n;++i)a[i]=max(a[i-1],in());
89     RG Node rt;rt.build(1,n);
90     while(q--){
91         op=in();x=in();y=in();
92         op?rt.updb(x):rt.bound(x);
93         printf("%lld\n",rt.pa*rt.pb%YL);
94     }
95     return 0;
96 }
View Code

 

posted @ 2019-04-02 14:51  hehe_54321  阅读(296)  评论(0编辑  收藏  举报
AmazingCounters.com