zoukankan      html  css  js  c++  java
  • 洛谷NOIp热身赛题解

    洛谷NOIp热身赛题解


    A 最大差值

    简单树状数组,维护区间和、区间平方和,方差按照给的公式算就行了

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    #define mod 1000000007
    typedef long long ll;
    namespace IO{
        const int maxn=(1<<21)+1;
        char ibuf[maxn],*iS,*iT,c;int f;
        inline char getc(){
            return iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,maxn,stdin),iS==iT?EOF:*iS++):*iS++;
        }
        inline int gi(){
            int x=0;
            for(f=1,c=getc();(c<'0'||c>'9');c=getc())f=c=='-'?-1:1;
            for(x=0;(c>='0'&&c<='9');c=getc())x=(x<<1)+(x<<3)+(c^48);
            return x*f;
        }
    }
    using IO::gi;
    int b[100010];
    int inv[100010];
    int sum[100010<<4],SUM[100010<<4],N;
    #define mid ((l+r)>>1)
    il vd build(int n){
        N=1<<(int)ceil(log(n+2)/log(2));
        for(int i=1;i<=n;++i)sum[i+N]=b[i],SUM[i+N]=1ll*b[i]*b[i]%mod;
        for(int i=N-1;i;--i)sum[i]=(sum[i<<1]+sum[i<<1|1])%mod,SUM[i]=(SUM[i<<1]+SUM[i<<1|1])%mod;
    }
    il vd update(int p,int d){
        p+=N,sum[p]=d,SUM[p]=1ll*d*d%mod;
        for(p>>=1;p;p>>=1)sum[p]=(sum[p<<1]+sum[p<<1|1])%mod,SUM[p]=(SUM[p<<1]+SUM[p<<1|1])%mod;
    }
    il std::pair<int,int> query(int l,int r){
        --l,++r;l+=N,r+=N;
        int ret=0,RET=0;
        while(l^r^1){
            if(~l&1)ret=(sum[l^1]+ret)%mod,RET=(SUM[l^1]+RET)%mod;
            if(r&1)ret=(sum[r^1]+ret)%mod,RET=(SUM[r^1]+RET)%mod;
            l>>=1;r>>=1;
        }
        return std::make_pair(ret,RET);
    }
    int main(){
        int n=gi(),m=gi();
        inv[1]=1;for(int i=2;i<=n;++i)inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
        for(int i=1;i<=n;++i)b[i]=gi();
        build(n);
        int o,l,r;
        while(m--){
            o=gi();
            if(o==1)l=gi(),r=gi(),update(l,r);
            else{
                l=gi(),r=gi();
                std::pair<int,int>A=query(l,r);
                int sa=A.first,SA=A.second;
                int pa=1ll*sa*inv[r-l+1]%mod;
                printf("%lld
    ",1ll*inv[r-l+1]*(1ll*pa*pa%mod*(r-l+1)%mod+SA-1ll*2*sa*pa%mod+mod)%mod);
            }
        }
        return 0;
    }
    

    B 攀爬者

    C 蜈蚣

    简单dp,设f[i][j]为1-j分成i段的最大收益,f[i][j]=max(f[i-1][k]+(A[k+1] xor ... xor A[j]))

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    typedef long long ll;
    il int gi(){
        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;
    }
    int f[2][1010],W[1010];
    il vd chkmx(int&a,int b){if(b>a)a=b;}
    int main(){
        int n=gi(),m=gi();
        register int x=0,i,j,k;
        for(i=1;i<=n;++i)W[i]=gi()^W[i-1];
        memset(f[x]+1,-127,n<<2);
        for(i=1;i<=m;++i){
            x^=1;
            memset(f[x],-127,i<<2);
            for(j=i;j<=n;++j){
                f[x][j]=0;
                for(k=i-1;k<=j;++k)
                    chkmx(f[x][j],f[x^1][k-1]+(W[j]^W[k-1]));
            }
        }
        printf("%d
    ",f[x][n]);
        return 0;
    }
    

    D 漂浮的鸭子

    同消息传递,略

    E 最大差值

    F 随机数生成器

    70分可以dp,设(f[i])表示(i)(1)的期望,那么(f[1]=0,f[i]=1+frac{sum_{j=1}^{i}f[j]}{i}(i>1))

    (frac{i-1}{i}f[i]=1+frac{sum_{j=1}^{i-1}f[j]}{i})

    然后100分可以发现(f[i]=1+(1/1+1/2+...+1/(i-1))),后面的调和级数可以直接套公式

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    typedef long long ll;
    il int gi(){
    	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;
    }
    int main(){
    	int n=gi();if(n==1)return puts("0.00000"),0;
    	double ans=1;
    	if(n<=1000000)for(int i=1;i<n;++i)ans+=1.0/i;
    	else ans=1+log(n-1)+0.5772156649015;
    	printf("%.5lf
    ",ans);
    	return 0;
    }
    

    G 大循环

    题面看着很nb,实际上上下的a没有关系,F(q)是个定值,答案就是(C_{n}^{k} imes F(q))

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    #define mod 1000000007
    #define int ll
    typedef long long ll;
    il ll gi(){
        ll 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;
    }
    int p[500010];
    il int pow(int x,int y){
        int ret=1;
        while(y){
            if(y&1)ret=1ll*ret*x%mod;
            x=1ll*x*x%mod;y>>=1;
        }
        return ret;
    }
    signed main(){
        ll n=gi(),m=gi(),k=gi(),q=gi()%mod,F=0;
        for(int x=1,i=0;i<=m;++i,x=1ll*x*q%mod)F=(F+1ll*x*gi()%mod)%mod;
        p[0]=1;for(int i=1;i<=n;++i)p[i]=1ll*p[i-1]*i%mod;
        printf("%lld
    ",1ll*p[n]*pow(1ll*p[k]*p[n-k]%mod,mod-2)%mod*F%mod);
        return 0;
    }
    

    H 会议座位

    I 生日礼物

    选两个数使得lcm=a,单独考虑a的每个质因数(p^k),那么这两个数一定有一个会有(p^k),另一个可以是(p^{(0-k)}),所以(p^k)(2k+1)中选法。给a分解质因数,(ans=Pi(2k+1))

    J HKE与他的小朋友

    K 宝藏

    二维树状数组 区间异或 区间查询异或和

    和一维树状数组区间修改区间查询一样,记(d(x,y))((x,y,n,n))这个子矩形的共同增量,查询((x_0,y_0))只需查询((1,1,x_0,y_0))的异或和。

    冷静分析,(d(x,y))((1,1,x_0,y_0))出现了奇数次才要计算(d(x,y)),条件就是((x_0-x+1)(y_0-y+1))为奇数,那么(x,x_0)同奇偶,(y,y_0)同奇偶,对于每一种(x,y)的奇偶情况开一个树状数组即可,一共4个

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    typedef long long ll;
    il int gi(){
        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;
    }
    int n,m,N;
    int t[2][2][1271][1271];
    il vd update(int x,int y,int d){
        int X=x&1,Y=y&1;x=(x+1)>>1,y=(y+1)>>1;
        for(int i=x;i<=N;i+=i&-i)
            for(int j=y;j<=N;j+=j&-j)
                t[X][Y][i][j]^=d;
    }
    il int query(int x,int y){
        int ret=0,X=x&1,Y=y&1;x=(x+1)>>1,y=(y+1)>>1;
        for(int i=x;i;i-=i&-i)
            for(int j=y;j;j-=j&-j)
                ret^=t[X][Y][i][j];
        return ret;
    }
    int main(){
        n=gi(),m=gi();N=(n+1)>>1;
        int x0,y0,x1,y1;char ch;
        for(int i=1;i<=m;++i){
            do ch=getchar();while(ch!='P'&&ch!='Q');
            x0=gi(),y0=gi(),x1=gi(),y1=gi();
            if(ch=='P'){
                int t=gi(),S=0,a,b;
                while(t--)a=gi(),b=gi(),S^=(b&1)<<(a-1);
                update(x0,y0,S);
                update(x0,y1+1,S);
                update(x1+1,y0,S);
                update(x1+1,y1+1,S);
            }else{
                int ans=query(x1,y1)^query(x1,y0-1)^query(x0-1,y1)^query(x0-1,y0-1);
                for(int i=0;i<30;++i)putchar('1'+((ans>>i)&1));
                puts("");
            }
        }
        return 0;
    }
    

    L 简单的函数

    回顾一下题目意思,对每个(x)找到最小的(t)满足(t otmid x)(f(x)=f(t)+1)

    倒过来考虑,对每个(t)会转移到多少个(x)。(称为(t)的答案)(x)如果从(t)转移过来,那么(lcm(1,2,...,t-1)mid x,lcm(1,2,...,t) otmid x)

    先考虑(lcm(1,2,...,t-1)|x),也就是说(x)(lcm(1,2,...,t-1))的倍数。满足这个的(x)(lfloorfrac{n}{lcm(1,2,...,t-1)} floor)个。还要减去(lcm(1,2,...,t)mid x)的情况,也就是所有(t'>t)的答案。

    注意到(lcm(1,...,42))大约是(2*10^{17}),那么可行的(t)不会超过(42),从(42)开始算即可。

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    #define mod 1000000007
    #define int ll
    typedef long long ll;
    il int gi(){
        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;
    }
    il ll lcm(ll a,ll b){return a/std::__gcd(a,b)*b;}
    ll f[]={0,0,1,2,3,2,4,2,3,2,3,2,3,2,3,2,3,2,4,2,3,2,3,2,3,2,3,2,3,2,4,2,3,2,3,2,3,2,3,2,3,2,4,2};
    il int pow(int x,int y){
        int ret=1;
        while(y){
            if(y&1)ret=1ll*ret*x%mod;
            x=1ll*x*x%mod;y>>=1;
        }
        return ret;
    }
    ll L[50],a[50];
    signed main(){
        int n=gi();
        L[1]=1;for(int i=2;i<=42;++i)L[i]=lcm(i,L[i-1]);
        ll ans=1,sum=0;
        for(int i=42;i;--i){
            a[i]=n/L[i]-sum;
            if(L[i]<=2)--a[i];
            if(i==1)--a[i];
            ans=1ll*ans*pow(f[i+1]+1,a[i])%mod;
            sum+=a[i];
        }
        printf("%lld
    ",ans);
        return 0;
    }
    

    M 数列游戏

    先dp一遍,(f[l][r])表示([l,r])这个区间里的数能否被消完,那么(f[i][i+1]=[gcd(A[i],A[i+1])!=1])

    (f[l][r])有两种转移

    1. (f[l+1][r-1] (gcd(A[l],A[r])!=1)),先消完([l+1,r-1]),再消掉(l,r)
    2. (f[l][k],f[k+1][r]),消掉([l,k])([k+1],r)

    这一段是(O(n^3))的(我也不知道为啥能过)

    然后就知道了哪些区间能用。剩下的看代码吧

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    typedef long long ll;
    il int gi(){
        int x=0;
        char ch=getchar();
        while(!isdigit(ch)){
            ch=getchar();
        }
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x;
    }
    ll A[1001],B[1001];
    bool f[810][810];
    ll g[810],sg[810];
    
    il void chkmax(ll &a,ll b){if (a<b)a=b;}
    
    int main(){
        register int n=gi(),i,l,r,s;
        for(i=1;i<=n;++i)A[i]=gi();
        for(i=1;i<=n;++i)B[i]=gi()+B[i-1];
        for(i=1;i<n;++i)if(std::__gcd(A[i],A[i+1])!=1)f[i][i+1]=1;
        for(s=4;s<=n;s+=2)
            for(l=1;l+s-1<=n;++l){
                r=l+s-1;
                if(std::__gcd(A[l],A[r])!=1&&f[l+1][r-1])f[l][r]=1;
                for(i=l+1;i<=r&&!f[l][r];i+=2)
                    f[l][r]|=f[l][i]&f[i+1][r];
            }
        for(r=1;r<=n;++r){//依次加入右端点为r的区间
        // g[r]表示区间[1,r]的最大收益,sg[r]=max(g[1],...,g[r])
            for(l=1;l<=r;++l)if(f[l][r])chkmax(g[r],B[r]-B[l-1]+sg[l-1]);
            for(i=1;i<=r;++i)sg[i]=std::max(sg[i-1],g[i]);
        }
        printf("%lld
    ",sg[n]);
        return 0;
    }
    
  • 相关阅读:
    麦卡姆轮运动原理
    ESP32开发(2)esp32-cam采集图像
    ESP32开发(1)环境配置
    Cesium学习笔记2-3:视频投影
    Cesium中实时显示经纬度及视角高
    Cesium学习笔记2-5:内部使用阳历扩展
    Cesium学习笔记2-4:外部扩展
    Cesium学习笔记2-4:更多官方示例
    win10通过wifi分享上网
    更换源地址
  • 原文地址:https://www.cnblogs.com/xzz_233/p/9927746.html
Copyright © 2011-2022 走看看