• 洛谷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
    ",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_ige 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(nlog n+qlog^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
    ",rt.pa*rt.pb%YL);
    94     }
    95     return 0;
    96 }
    View Code
  • 相关阅读:
    众皓网络(T 面试)
    骑芯供应链(T 面试)
    骑芯供应链(W 笔试)
    面试问题_一拉到底
    Java后端学习路线_备战
    docker 容器
    技术展望
    索引 命令
    索引 概念原理
    面试技能更新
  • 原文地址:https://www.cnblogs.com/hehe54321/p/10639721.html
Copyright © 2020-2023  润新知