• Educational Codeforces Round 65 E,F


    E. Range Deleting

    题意:给出一个序列,定义一个操作f(x,y)为删除序列中所有在[x,y]区间内的数。问能使剩下的数单调不减的操作f(x,y)的方案数是多少。

    解法:不会做,思维跟不上,双指针也不熟练。思路和代码都是学习https://edwiv.com/archives/587这位巨佬的。

    说下我的理解:我们考虑怎样的操作[l,r]才是合理的?很容易能想到有3个条件:①删除后剩下的数字[1,l-1]的位置是单调递增,②数字[r+1,x]的位置也是单调递增的,3 数字l-1的所有位置都要比r+1小。那么我们的任务就是预处理这个序列使得能快速判断这3个条件。

    posmin[maxn] posmax[maxn]表示每个数下标的最小值和最大值
    premax[maxn] sufmin[maxn]表示[1,i]/[i,x]范围内的数出现的下标值的最大值/最小值
    precan[maxn] sufcan[maxn]表示[1,i]是否合法,[i,x]是否合法

    然后判断[l,r]是否是一个合理操作的条件就是:precan[l-1] && sufcan[r+1] && (premax[l-1]<sufmin[r+1]);  //这分别对应上面的3个条件

    那么到这里我们就能够O(1)快速判断某个操作是否合法,接下来就是统计答案。当然不能n^2统计会超时,这里用到双指针的技巧,枚举左端点l,右端点r由上一个左端点l-1的r继承而来,之后再实际判断移动得到当前左端点l的应该右端点r,就可以统计答案贡献就是x-r+1咯。这里的正确性是基于:[l,r]是合理的那么l+1的r必定大于l的r,左右端点是同步递增的。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long LL;
     4 const int N=1e6+10;
     5 int n,x,a[N];
     6 int posmin[N],posmax[N],premax[N],sufmin[N];
     7 bool precan[N],sufcan[N];
     8 
     9 bool check(int l,int r) {  //判断删去区间[l,r]后是否得到合理答案 
    10     return precan[l-1] && sufcan[r+1] && (premax[l-1]<sufmin[r+1]);
    11 }
    12 
    13 int main()
    14 {
    15     cin>>n>>x;
    16     memset(posmin,0x3f,sizeof(posmin));
    17     memset(posmax,0,sizeof(posmax));
    18     for (int i=1;i<=n;i++) {
    19         scanf("%d",&a[i]);
    20         posmin[a[i]]=min(posmin[a[i]],i);
    21         posmax[a[i]]=max(posmax[a[i]],i);
    22     }
    23     for (int i=1;i<=x;i++) premax[i]=max(premax[i-1],posmax[i]);
    24     sufmin[x+1]=n+1; for (int i=x;i;i--) sufmin[i]=min(sufmin[i+1],posmin[i]);
    25     memset(precan,0,sizeof(precan)); precan[0]=1;
    26     for (int i=1;i<=x;i++) precan[i]=precan[i-1]&&(posmin[i]>premax[i-1]);
    27     memset(sufcan,0,sizeof(sufcan)); sufcan[x+1]=1;
    28     for (int i=x;i;i--) sufcan[i]=sufcan[i+1]&&(posmax[i]<sufmin[i+1]);
    29     
    30     LL ans=0;
    31     int l=1,r=1;  //双指针 
    32     for (;l<=x;l++) {  //左指针遍历 
    33         if (l>r) r=l;
    34         while (r<x && !check(l,r)) r++;  //移动右指针
    35         if (check(l,r)) ans+=(x-r+1);  //累加左指针为l时候的贡献为(x-r+1)
    36     }
    37     cout<<ans<<endl;
    38     return 0;
    39 }
    View Code

    F. Scalar Queries

    解法:虽然能猜到是算排名贡献乘以数字得到答案,但是还是没做出来qwq。参考https://www.cnblogs.com/carcar/p/10877964.html这位巨佬的。

    讲一下自己的理解:容易发现其实答案就是算d[i]*a[i]。这个d[i]系数其实就是所有包含a[i]这个数的区间的a[i]的排名总和。那么怎么样才能快速算得这个d[i]?我们从贡献这个角度思考:

    在a[i]的左边,只有a[j]<a[i](j<i)的时候a[j]对a[i]才会有提升排名的作用,并且这个提升一个排名的效果在所有包含了(a[j]和a[i])的区间都有效。

    同理的,在a[i]的右边,只有a[j]<a[i](j<i)的时候才有提升排名的作用,并且在所有包含a[j]和a[i]的区间有效。

    然后对于a[i]自己也是同理,自己给自己提升了一个排名。

    那么我们怎么快速算 a[j]<a[i] 且所有包含了a[j] a[i]的区间个数呢?以a[i]左边为例分析,仔细观察发现其实区间个数就是j*(n-i+1),对于每个a[j]这个因子j是不会改变的,然后对于a[i]这个因子(n-i+1)也是不会改变的。我们要做的就是快速统计所有a[j]<a[i]的因子j的总和,嗯?这不就是树状数组。对,我们从左到右扫一遍利用树状数组统计,从右往左扫一遍,统计得出d[i]之后此题就解决了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long LL;
     4 const int N=5e5+10;
     5 const int P=1e9+7;
     6 int n,m,a[N],b[N],rk[N];
     7 LL d[N];
     8 
     9 LL sum[N];
    10 void update(int x,int v) {
    11     for (;x<=n;x+=x&-x) sum[x]+=v,sum[x]%=P;
    12 }
    13 LL query(int x) {
    14     LL ret=0;
    15     for (;x;x-=x&-x) ret+=sum[x],ret%=P;
    16     return ret;
    17 }
    18 
    19 int main()
    20 {
    21     cin>>n;
    22     for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
    23     sort(b+1,b+n+1);
    24     for (int i=1;i<=n;i++) rk[i]=lower_bound(b+1,b+n+1,a[i])-b;
    25     
    26     for (int i=1;i<=n;i++) {  //计算[1,i-1]区间的贡献 
    27         d[i]=(d[i]+query(rk[i]-1)*(n-i+1)%P)%P;
    28         update(rk[i],i);
    29     }
    30     for (int i=1;i<=n;i++) d[i]=(d[i]+(LL)i*(n-i+1)%P)%P;  //计算[i,i]的贡献 
    31     memset(sum,0,sizeof(sum));
    32     for (int i=n;i;i--) {  //计算[i+1,n]的贡献 
    33         d[i]=(d[i]+query(rk[i]-1)*(i)%P)%P;
    34         update(rk[i],n-i+1);
    35     }
    36     
    37     LL ans=0;
    38     for (int i=1;i<=n;i++) ans=(ans+a[i]*d[i]%P)%P;
    39     cout<<ans<<endl;
    40     return 0;
    41 }
    View Code
  • 相关阅读:
    查找父/子控件(元素、节点)
    【转载】单点系统架构的可用性与性能优化
    【转载】互联网架构,如何进行容量设计?
    【转载】细聊分布式ID生成方法
    【转载】秒杀系统架构优化思路
    【转载】程序员这口饭-职业规划解决方案
    【转载】一位软件工程师的6年总结
    【转载】VS工具使用——代码生成函数关系图
    【转载】VS工具使用——代码图
    【转载】一些VS2013的使用技巧
  • 原文地址:https://www.cnblogs.com/clno1/p/11200143.html
Copyright © 2020-2023  润新知