zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试52]题解

    A.平均数

    看到第K小,又确定跟平衡树/主席树没有关系,可以把问题转化为有K-1个答案比它小再考虑二分。

    二分平均值x,之后将原序列统一减去x。这时序列中区间和<0的区间个数就是原序列中平均值小于x的区间个数。

    求个前缀和,那么区间和<0转化成$sum_l > sum_r$,归并排序求逆序对即可。复杂度$O(n log^2 n)$

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    const int N=1e5+5;
    const double eps=1e-8;
    int n,K,num;
    double a[N],b[N],sum[N],c[N];
    void merge(int l,int r)
    {
        if(l==r)return ;
        int mid=l+r>>1;
        merge(l,mid);merge(mid+1,r);
        int i=l,j=mid+1,k=l;
        while(i<=mid&&j<=r)
        {
            if(sum[i]>sum[j])num+=mid-i+1,c[k]=sum[j],j++;
            else c[k]=sum[i],i++;
            k++;
        }
        while(i<=mid)c[k]=sum[i],i++,k++;
        while(j<=r)c[k]=sum[j],j++,k++;
        for(int i=l;i<=r;i++)sum[i]=c[i];
    }
    
    bool check(double val)
    {
        sum[0]=0;num=0;
        for(int i=1;i<=n;i++)
            a[i]=b[i]-val,sum[i]=sum[i-1]+a[i];
        merge(0,n);
        return num<K;
    }
    
    int main()
    {
        n=read();K=read();
        double l=0,r=0;
        for(int i=1;i<=n;i++)
            b[i]=(double)read();
        r=1e9+1.0;
        while(r-l>eps)
        {
            double mid=(l+r)/2.0;
            if(check(mid))l=mid;
            else r=mid;
        }
        printf("%.4lf
    ",l);
        return 0;
    }
    

    B.涂色游戏

    不会。勉强理解题解之后过掉的。矩阵快速幂一直不能游刃有余地运用到dp优化里,这布星啊。

    最裸的矩阵快速幂。

    这一行的填色方案只与上一行有关,所以dp。

    $dp[i][j]$表示在第i行填了特定j中颜色的方案数。

    考虑转移。枚举两行的颜色数再枚举交集即可。

    如果上一行有i种下一行有j种交集为k种。

    转移条件是$i+j-k<=p and i+j-k>=q$。

    那么就是一个组合数学问题了。

    先在上一行的颜色里选出重复部分Cki

    再选出这一行交集以外的部分Cjkpi

    然后的问题就是已知某k种颜色填n个位置,要求每种颜色必须出现。我是dp做的,据说可以容斥。

    然后发现每一层的转移系数都相同,那就是简单的矩阵快速幂了。

    注意取模。

      ——DeepinC

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const ll mod=998244353;
    const int N=105,M=2005;
    int n,p,q;
    ll m,C[N][N],A[N][N],b[N][N],res[N],tmp[N][N],tp[N];
    void b_mult()
    {
        memset(tmp,0,sizeof(tmp));
        for(int i=1;i<=p;i++)
            for(int j=1;j<=p;j++)
                for(int k=1;k<=p;k++)
                    (tmp[i][j]+=b[i][k]*b[k][j]%mod)%=mod;
        for(int i=1;i<=p;i++)
            for(int j=1;j<=p;j++)
                b[i][j]=tmp[i][j];
    }
    
    void res_mult()
    {
        memset(tp,0,sizeof(tp));
        for(int i=1;i<=p;i++)
            for(int j=1;j<=p;j++)
                (tp[i]+=res[j]*b[j][i]%mod)%=mod;
        for(int i=1;i<=p;i++)
            res[i]=tp[i];
    }
    int main()
    {
        scanf("%d%lld%d%d",&n,&m,&p,&q);
        C[0][0]=A[0][0]=1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=p;j++)
                A[i][j]=(A[i-1][j-1]+A[i-1][j])*j%mod;
        for(int i=1;i<=p;i++)
        {
            C[i][0]=1;
            for(int j=1;j<=i;j++)
                (C[i][j]=C[i-1][j-1]+C[i-1][j])%=mod;
        }
        for(int i=1;i<=p;i++)
            for(int j=1;j<=p;j++)
                for(int k=0;k<=p;k++)
                    if(i+j-k<=p&&i+j-k>=q)
                        (b[i][j]+=C[i][k]*C[p-i][j-k]%mod*A[n][j])%=mod;
        for(int i=1;i<=p;i++)
            res[i]=A[n][i]*C[p][i]%mod;
        m--;
        while(m)
        {
            if(m&1)res_mult();
            b_mult();
            m>>=1;
        }
        ll ans=0;
        for(int i=1;i<=p;i++)
            (ans+=res[i])%=mod;
        printf("%lld
    ",ans);
        return 0;
    }
    

    C.序列

    先用主席树求第一问。

    我们注意到,每次修改后对答案作贡献其实只有范围包括修改点的询问,并且改小了做正贡献,改大了做负贡献。

    对于询问建立一棵线段树,下标为询问控制的区间,对每一个结点开一个vector,把所询问的值添加进线段树对应的节点。

    将询问按询问值排序,之后按顺序全部更新到线段树中。排序是为了让每个节点的vector内部元素保证有序,确保可以二分查找。

    之后对于每次修改,在线段树里查一下改之前该位置的贡献(单点查询,路过每个包含的区间时在它的vector里二分查找),再查一下改之后的,二者做差更新答案,然后在外部数组里更改即可。

    复杂度$O(n log^2 n)$。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    const int N=1e5+5;
    typedef long long ll;
    int n,m,Q,a[N];
    struct ques
    {
        int l,r,val;
        friend bool operator < (ques x,ques y)
        {
            return x.val<y.val;
        }
    }q[N];
    int ls[N*40],rs[N*40],size[N*40],type,root[N];
    ll ans;
    void update(int &k,int l,int r,int old,int val)
    {
        k=++type;
        ls[k]=ls[old];
        rs[k]=rs[old];
        size[k]=size[old]+1;
        if(l==r)return ;
        int mid=l+r>>1;
        if(val<=mid)update(ls[k],l,mid,ls[old],val);
        else update(rs[k],mid+1,r,rs[old],val);
    }
    int query(int k,int l,int r,int val)
    {
        if(l==r)return size[k];
        int mid=l+r>>1,res=0;
        if(val<=mid)return size[rs[k]]+query(ls[k],l,mid,val);
        else return query(rs[k],mid+1,r,val);
    }
    vector<int> w[N<<2];
    #define ls(k) (k)<<1
    #define rs(k) (k)<<1|1
    void add(int k,int l,int r,int L,int R,int val)
    {
        if(L<=l&&R>=r)
        {
            w[k].push_back(val);
            return ;
        }
        int mid=l+r>>1;
        if(L<=mid)add(ls(k),l,mid,L,R,val);
        if(R>mid)add(rs(k),mid+1,r,L,R,val);
    }
    int ask(int k,int l,int r,int pos,int val)
    {
        int res=upper_bound(w[k].begin(),w[k].end(),val)-w[k].begin()-1;
        if(l==r)return res;
        int mid=l+r>>1;
        if(pos<=mid)res+=ask(ls(k),l,mid,pos,val);
        else res+=ask(rs(k),mid+1,r,pos,val);
        return res;
    }
    int main()
    {
        n=read();m=read();Q=read();
        for(int i=1;i<=n;i++)
            a[i]=read(),update(root[i],1,n,root[i-1],a[i]);
        for(int i=1;i<=m;i++)
        {
            q[i].l=read(),q[i].r=read(),q[i].val=read();
            int res1=query(root[q[i].r],1,n,q[i].val),res2=query(root[q[i].l-1],1,n,q[i].val);
            ans+=res1-res2;
        }
        sort(q+1,q+m+1);
        for(int i=1;i<=m;i++)
            add(1,1,n,q[i].l,q[i].r,q[i].val);
        printf("%lld
    ",ans);
        while(Q--)
        {
            int p=read()^ans,v=read()^ans;
            int res1=ask(1,1,n,p,a[p]),res2=ask(1,1,n,p,v);
            ans+=res2-res1;
            a[p]=v;
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    Redis缓存穿透、缓存雪崩、redis并发问题 并发竞争key的解决方案 (阿里)
    CAP原则 (阿里)
    分布式系统一致性问题解决实战(阿里) 异步解耦+消息队列可作为分布式系统满足最终一致性的优秀方案
    高并发&高可用系统的常见应对策略 秒杀等-(阿里)
    Redis的内存回收策略和内存上限(阿里)
    如何保证MQ的顺序性?比如Kafka
    spring的controller默认是单例还是多例
    HashMap线程不安全的体现
    TCP的三次握手与四次挥手
    常见排序算法及对应的时间复杂度和空间复杂度
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11603195.html
Copyright © 2011-2022 走看看