2019.9.26 csp-s模拟测试52 反思总结

刚刚写了一个小时的博客没了,浏览器自动刷新。

一!个!小!时!

鼠标键盘电脑哪个都不能摔,气死我了。

垃圾选手T1T2没思路,T3倒是想出来得比较早,靠T3撑着分数。

数据结构学傻选手,属实垃圾。

T1平均数:

一个序列的所有数如果减去x,那么平均数也会减去x。可以二分这个x,统计序列里平均数小于0的序列的个数,含义为原序列平均数小于x的序列的个数。最后统计值小于k且最接近k的x就是所求答案。

序列的平均数小于0,那么序列的和也一定小于0。表现在前缀和上即为一个区间的sumr<suml-1,转化为求前缀和序列的逆序对个数。

归并排序的时候前缀和数组下标从0开始,因为可能存在序列开始的一段就小于0的情况,要和sum0比较。

#include<iostream>
#include<cstdio>
using namespace std;
int n,k;
double ans;
int num;
double b[100010],c[100010],s[100010],a[100010],maxx;
void work(int l,int r){
    if(l==r){
        b[l]=s[l];
        return;
    }
    int mid=(l+r)/2;
    work(l,mid);
    work(mid+1,r);
    int ll=l,rr=mid+1,p=l;
    while(ll<=mid&&rr<=r){
        if(b[ll]<=b[rr]){
            c[p++]=b[ll++];
        }
        else{
            num+=mid-ll+1;
            c[p++]=b[rr++];
        }
    }
    while(ll<=mid){
//        num+=r-mid;
        c[p++]=b[ll++];
    }
    while(rr<=r){
        c[p++]=b[rr++];
    }
    for(int i=l;i<=r;i++){
        b[i]=c[i];
    }
}
int check(double x){
    num=0;
    for(int i=1;i<=n;i++){
        s[i]=s[i-1]+a[i]-x;
    }
    work(0,n);
    return num;
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%lf",&a[i]);
        maxx=max(maxx,a[i]);
    }
    double l=0,r=1e9;
    while(l+1e-5<r){
        double mid=(l+r)*0.5;
        if(check(mid)<k){
            l=mid;
        }
        else r=mid;
    }
    printf("%.4lf",l);
    return 0;
} 

T2涂色游戏:

考试的时候先想到矩阵快速幂,然后不会推DP式子……

因为相邻两列涂q种颜色与具体是哪种颜色无关,转化成关于列的DP问题。

先求出一列涂i种颜色的方案数。设f[i][j]为一列里到第i行涂了j种颜色的方案数,f[i][j]=f[i-1][j-1]*(p-(j-1))+f[i-1][j]*j,含义是从剩下的p-(j-1)种颜色中选一个新颜色涂,或者从已有的j种颜色中选一个重复。设g[i]=f[n][i],g[i]即一列涂i种颜色的方案数。

考虑从上一列的j种颜色转移到这一列的k种颜色。对于每一种确定的用了j种颜色的涂色方案,先枚举j和k的并集x。那么这种情况下k可以分成两部分,与j交集的部分有C(j,j+k-x)种选择,k剩下的颜色有C(p-j,x-j)种选择。然后对于每一种确定了k种颜色的选择方案,有g[k]/C(p,k)种涂色方案。

那么每一种确定的j转移给k的方案数就是,再乘上一个dp[i-1][j],即上一列j种颜色的总方案数。

初始化是dp[1][i]=g[i]。

发现对于不同列的j到k的转移,乘上的参数是一样的,那么就可以用矩阵快速幂优化。

答案矩阵的初始化是dp[1][i],转移矩阵的a[i][j]即为i转移到j的参数,然后进行矩阵快速幂m-1次(从第二列开始)。

#include<iostream>
#include<cstdio>
using namespace std;
const int mod=998244353;
int n,m,p,q;
long long f[110][110],c[110][110],dp[1010][110],ans;
struct node{
    long long g[110][110];
}a,b,d;
long long ks(long long x,long long k){
    long long num=1;
    while(k){
        if(k&1)num=num*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return num;
}
long long cal(int j,int k){
    long long num=0;
    for(int x=max(q,max(j,k));x<=min(p,j+k);x++){
        num=(num+(c[j][j+k-x]*c[p-j][x-j]%mod)%mod)%mod;
    }
    return num;
}
node cheng(node x,node y){
    node e;
    for(int i=1;i<=p;i++){
        for(int j=1;j<=p;j++){
            e.g[i][j]=0;
        }
    }
    for(int k=1;k<=p;k++){
        for(int i=1;i<=p;i++){
            for(int j=1;j<=p;j++){
                e.g[i][j]=(e.g[i][j]+x.g[i][k]*y.g[k][j]%mod)%mod;
            }
        }
    }
    return e;
}
void ksm(int k,node x){
    while(k){
        if(k&1)b=cheng(b,x);
        x=cheng(x,x);
        k>>=1;
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&p,&q);
    c[0][0]=1;
    for(int i=1;i<=100;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;
        }
    }
    f[0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=p;j++){
            f[i][j]=(f[i-1][j]*j%mod+f[i-1][j-1]*(p-(j-1))%mod)%mod;
        }
    }
    for(int i=1;i<=p;i++){
        for(int j=max(q-i,1);j<=min(n,p);j++){
            a.g[i][j]=(a.g[i][j]+f[n][j]*ks(c[p][j],mod-2)%mod*cal(i,j)%mod)%mod;
        }
    }
    for(int i=1;i<=p;i++){
        b.g[1][i]=f[n][i];
    }
    m--;
    ksm(m,a);
    for(int i=1;i<=p;i++){
        ans=(ans+b.g[1][i])%mod;
    }
    printf("%lld",ans);
    return 0;
}

T3序列:

一边写题解一边复习这些题才搞了一个多小时,第二遍写倒是挺快的。然后T3可能将是今天写题解耗时最少的题……?

看到强制在线,第一反应是答案由上一个答案转移而来。

n和q的复杂度都是1e5级别,肯定要优化其中一个到log。因为答案很可能从上一个进行变化而来,每次修改是单点的,询问的具体内容又不变,考虑怎么才能存下询问的全部信息并快速找出每次修改影响了哪些询问。

询问是区间询问的形式,考虑存下哪个询问的x覆盖了哪些范围,并快速得知每次修改的位置会影响哪些x。发现对于每次修改,覆盖了修改位置的x中,受到影响的x在一定范围内,即修改位置的原数与现在的数的中间这个范围。因为每次只修改一个位置,对于一个询问若存在影响只能是答案+1或-1,所以如果能够快速查出多少个x在这个受到影响的范围内就能得知答案进行了多少变化。于是把询问的左右端点拆开,像差分一样,l处记录x的+1,r+1处记录x的-1。然后用一棵下标是x的值的主席树,保存序列每个位置受到哪些x的覆盖。

对于每次修改,只要查出修改位置有多少个x的值在修改值和原值中间而受到影响,对答案进行相应加减即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,q,cnt1,pre=1;
int a[100010],tree[100010];
long long ans;
struct node{
    int pos,x,val;
}b[200010];
struct node1{
    int l,r,x;
}c[200010];
bool cmp(node x,node y){
    if(x.pos<y.pos)return true;
    else return false;
}
int T[300010*18],L[300010*18],R[300010*18],tot,cnt[300010*18];
void build(int &p,int l,int r){
    p=++tot;
    if(l==r)return;
    int mid=(l+r)/2;
    build(L[p],l,mid);
    build(R[p],mid+1,r);
}
void update(int &p,int pre,int l,int r,int x,int val){
    p=++tot;
    L[p]=L[pre],R[p]=R[pre],cnt[p]=cnt[pre]+val;
    if(l==r)return;
    int mid=(l+r)/2;
    if(x<=mid)update(L[p],L[pre],l,mid,x,val);
    else update(R[p],R[pre],mid+1,r,x,val);
}
void add(int x){
    for(;x<=n;x+=(x&-x))tree[x]++;
}
int ask(int x){
    if(!x)return 0;
    int val=0;
    for(;x;x-=(x&-x))val+=tree[x];
    return val;
}
int query(int p,int l1,int r1,int l,int r){
    if(l1<=l&&r<=r1)return cnt[p];
    int mid=(l+r)/2;
    if(r1<=mid)return query(L[p],l1,r1,l,mid);
    if(l1>mid)return query(R[p],l1,r1,mid+1,r);
    return query(L[p],l1,mid,l,mid)+query(R[p],mid+1,r1,mid+1,r);
}
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1,x,y,z;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        b[++cnt1].pos=x,b[cnt1].x=z,b[cnt1].val=1;
        b[++cnt1].pos=y+1,b[cnt1].x=z,b[cnt1].val=-1;
        c[i].l=x,c[i].r=y,c[i].x=z;
    }
    sort(b+1,b+2*m+1,cmp);
    cnt1=1;
    build(T[0],1,n);
    for(int i=1;i<=n+1;i++){
        if(b[cnt1].pos==i){
            while(b[cnt1].pos==i){
                update(T[i],pre,1,n,b[cnt1].x,b[cnt1].val);
                pre=T[i];
                if(b[cnt1].val==-1){//r
                    ans+=ask(n)-ask(b[cnt1].x-1);
                }
                else{//l
                    ans-=ask(n)-ask(b[cnt1].x-1);
                }
                cnt1++;
            }
        }
        else if(i<=n){
            T[i]=pre;
        }
        if(i<=n)add(a[i]);
    }
    printf("%lld\n",ans);
    for(int i=1,p,v;i<=q;i++){
        scanf("%d%d",&p,&v);
        long long pos=p^ans;
        long long x=v^ans;
        if(x>a[pos]){
            int num=query(T[pos],a[pos]+1,x,1,n);
            ans+=num;
            a[pos]=x;
        }
        else if(x<a[pos]){
            int num=query(T[pos],x+1,a[pos],1,n);
            ans-=num;
            a[pos]=x;
        }
        printf("%lld\n",ans);
    }
    return 0;
 } 

再也不用垃圾浏览器写题解了orzzzzzzzz气死我了

一晚上就写这一篇了,虽然连带了漫长的复习时间,重新搞懂这些题并努力总结套路…

咕掉的题解还有n篇,我好难——

原文地址:https://www.cnblogs.com/chloris/p/11614198.html

时间: 2024-10-09 06:24:43

2019.9.26 csp-s模拟测试52 反思总结的相关文章

2019.10.26 csp-s模拟测试88 反思总结

今天的主人公是什么? 60.1K!!!! 先扔代码再更新防止我等会儿一上头不打算写完题解 T1: #include<iostream> #include<cstdio> #include<algorithm> using namespace std; const double inf=214748364; int n,k; double a[100010],f[21][100010]; int main() { scanf("%d%d",&n

模拟测试52,53反思

这两次考试都挂了不少分,也学到了很多东西. 52 T1 常数写大正解T成暴力 T2 数组越界70->50又由于我的智障操作最后一秒50->20 53 T2 逆推打成正推100->21 T3 暴力56pts,错解57pts,机智的我交了暴力,还把数组开小了57->43. 1.虽然不同算法的理论复杂度是一样的,但是常数真的很重要!map常数很大! 2.在不确定自己打的是对的时候,不要在最后时刻交代码. 3.期望逆着推,其实不是期望,对于最优决策来说,要么枚举所有决策, 判断优劣,要么先

2019.10.26 CSP%您赛第三场

\(CSP\)凉心模拟^_^ --题源\(lqx.lhc\)等各位蒟蒻 题目名称 比赛 传递消息 开关灯 源文件名 \(competition.cpp\) \(message.cpp\) \(light.cpp\) 输入文件名 \(competition.in\) \(message.in\) \(light.in\) 输出文件名 \(competition.out\) \(message.out\) \(light.out\) 测试点时限 \(1s\) \(1s\) \(2s\) 内存限制 \

2019.9.28 csp-s模拟测试54 反思总结

咕咕咕的冲动如此强烈x T1x: 看完题目想了想,感觉把gcd不为1的强行放在一组,看作一个连通块,最后考虑连通块之间的组合方式就可以了. 然后维护这个连通块可以写并查集可以连边跑dfs怎么着都行… 然而我在处理数字分解质因数这里T掉了,原因是一个很显然的优化写法我基本没怎么写过.线性筛的时候记录每个数是被哪个质数标记过的,分解一个数的时候直接处理记录下来的质数就可以. #include<iostream> #include<cstdio> #include<cmath>

2019.10.22 csp-s模拟测试82 反思总结

算了 我在干什么orz T2: #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=1e5+10,p=1500007,mod=998244353; int n,len,cnt1=1,cnt2=1,lens; char s[2*N],c[2*N]; int tree1[2*N][27],tree2[2*N][27]; unsigned long lon

2019.10.30 csp-s模拟测试94 反思总结

头一次做图巨的模拟题OWO 自从上一次听图巨讲课然后骗了小礼物以后一直对图巨印象挺好的233 T1: 对于XY取对数=Y*log(x) 对于Y!取对数=log(1*2*3*...*Y)=log1+log2+log3+...+logY 因为数字大小不超过1e5,直接累加最后比较就可以了 #include<iostream> #include<cstdio> #include<cmath> using namespace std; int t,x,y; double a,b

2019.9.19 csp-s模拟测试47 反思总结

思路接近正解?都想到了?这都是借口呀. 没有用的,往前走吧. T1:Emotional Flutter 我的做法和题解不太一样,我把s放在最后考虑了. 因为出发以后步幅是一样的,所以每一个黑条可以ban掉一段出发点.把黑条的左右边界%k存成区间,每个黑条可以存一个或者两个区间[跨越k这个边界].然后像以前写区间覆盖的贪心一样按左端点排序,看看有没有长至少为s的空余. 代码: #include<iostream> #include<cstdio> #include<cstrin

2019.9.20 csp-s模拟测试48 反思总结

头疼,不说废话了,祝大家rp++. T1: 暴力枚举,n3. 枚举两个串开始匹配的位置,每一次尽量修改. #include<iostream> #include<cstdio> using namespace std; int n,k,cnt,num,ans; char a[310],b[310]; int main() { scanf("%d%d",&n,&k); scanf("%s",a+1); scanf("%

csp-s模拟测试52平均数,序列题解

题面:https://www.cnblogs.com/Juve/articles/11602244.html 平均数: 第k个平均数不好求,我们考虑二分,转化成平均数小于x的有几个 虑把序列中的每个数减去 x,则我们只需求区间和小于 0 的区间数量. 我们对这个序列求前缀和,则区间[L,R]和小于 0 当且仅当 SL-1>SR, 答案即为前缀和序列 S 的逆序对数量,使用经典的归并排序即可解决 #include<iostream> #include<cstdio> #incl