[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$。
那么就是一个组合数学问题了。
先在上一行的颜色里选出重复部分C
再选出这一行交集以外的部分C
然后的问题就是已知某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;
}

浙公网安备 33010602011771号