[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\n",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\n",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\n",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\n",ans);
    }
    return 0;
}

 

posted @ 2019-09-28 15:09  Rorschach_XR  阅读(173)  评论(0编辑  收藏  举报
//雪花飘落效果