zoukankan      html  css  js  c++  java
  • 牛客NOIP暑期七天营-提高组6

    比赛链接

    官方题解

    A-积木大赛

    题解

    标签:模板题,一点点思维+二维差分+二维前缀和

    借这道题机会复习了一下二维差分+二维前缀和这两个东西。

    首先题目让我们求总的表面积,实际上可以拆分成两部分:上下表面积+前后左右表面积

    1.上下表面积的话,根据俯视图想一想,就是有被积木覆盖额面积(*2)

    2.前后左右表面积的话,感性理解一下,只要他的高度大于他的前/后/左/右的积木高度,那么它这一块就可以露出来,所以就是与前后左右积木高度的差值。

    这两个问题都要用到在某一坐标积木的高度。那只要二维差分一下,最后统计一下二维前缀和,就可求得那个位置上积木的高度了。二维差分可以自己根据求二维前缀和的过程推一下。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    bool nc1;
    const int N=5010;
    int p[N][N],n,m,q;
    const int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
    inline bool ok(int x,int y){
        if(x<1||y<1||x>n||y>m)return 0;
        return 1;
    }
    bool nc2;
    int main(){
        //cout<<(&nc2-&nc1)/1024<<endl;
        scanf("%d%d%d",&n,&m,&q);
        while(q--){
            int a,b,c,d;
            scanf("%d%d%d%d",&a,&b,&c,&d);
            p[a][b]++;
            p[a][d+1]--;
            p[c+1][b]--; 
            p[c+1][d+1]++;
        }
        for(register int i=1;i<=n;i++)
            for(register int j=1;j<=m;j++)p[i][j]=p[i-1][j]+p[i][j-1]+p[i][j]-p[i-1][j-1];
        long long up=0,ans=0;
        for(register int i=1;i<=n;i++)for(register int j=1;j<=m;j++){
            if(!p[i][j])continue;
            up++;
            int tmp=ans;
            for(register int k=0;k<=3;k++){
                int x=dx[k]+i,y=dy[k]+j;
                if(!ok(x,y))ans+=p[i][j];
                else if(p[i][j]>p[x][y])ans+=p[i][j]-p[x][y];
            }
        }
        printf("%lld
    ",2*up+ans);
    }
    

    B-破碎的序列

    题解

    标签:化简问题+计数Dp

    先看看题面当她开心时(m=1)她喜欢不存在长度为奇数且大于1的回文子串的数列,当她不开心时(m=0)她喜欢不存在长度为偶数且大于1的回文子串的数列 。仔细想想根本不用搞什么回文串,化简题意后就是:

    当m=1时:(a[i]!=a[i+2])

    当m=0时:(a[i]!=a[i+1])

    看数据范围(n,k<=2e5),很明显是个计数Dp(当然也可以不用Dp,直接用数学方法计算,但Dp比较直观暴力)。定义状态(dp[i][0/1])表示现在合法地填完了数列的前(i)项,且第(i)项与(nxt[i])是否相同(0/1)时的方案数。

    完整代码如下,可以在理解cas1的前提下再去看cas2,总时间复杂度为(O(N))

    #include<bits/stdc++.h>
    #define mod 998244353
    using namespace std;
    const int N=2e5+10;
    int n,m,k,a[N],nxt[N];
    int dp[N][2];
    void cas1(){//a[i]!=a[i+1]
    	for(int i=n;i>=1;i--){
    		if(!a[i+1])nxt[i]=nxt[i+1];
    		else nxt[i]=a[i+1];
    	}
    	if(a[1]&&a[1]==nxt[1])dp[1][1]=1;
        else if(a[1])dp[1][0]=1;
    	else{dp[1][1]=1;dp[1][0]=k-1;}
    	for(int i=2;i<=n;i++){
    		if(a[i]&&a[i]==nxt[i])dp[i][1]=dp[i-1][0];
    		else if(a[i])dp[i][0]=dp[i-1][0]; 
    		else{
    			dp[i][1]=dp[i-1][0];
    			dp[i][0]=1ll*dp[i-1][0]*(k-2)%mod;
    			dp[i][0]+=1ll*dp[i-1][1]*(k-1)%mod;
    			dp[i][0]%=mod;
    		}
    	}
        printf("%d
    ",(dp[n][0]+dp[n][1])%mod);
    }
    void cas2(){//a[i]!=a[i+2]
    	for(int i=n-2;i>=1;i--){
    		if(!a[i+2])nxt[i]=nxt[i+2];
    		else nxt[i]=a[i+2];
    	}
    	for(int i=1;i<=2;i++){
    		if(a[i]&&a[i]==nxt[i])dp[i][1]=1;
    		else if(a[i])dp[i][0]=1;
    		else{dp[i][1]=1;dp[i][0]=k-1;}
    	}
    	for(int i=3;i<=n;i++){
    		if(a[i]&&a[i]==nxt[i])dp[i][1]=dp[i-2][0];
    		else if(a[i])dp[i][0]=dp[i-2][0];
    		else{
    			dp[i][1]=dp[i-2][0];	
    			dp[i][0]=1ll*dp[i-2][0]*(k-2)%mod;
    			dp[i][0]+=1ll*dp[i-2][1]*(k-1)%mod;
    			dp[i][0]%=mod;
    		}
    	}
    	int res1=(dp[n-1][0]+dp[n-1][1])%mod,res2=(dp[n][0]+dp[n][1])%mod;
    	printf("%d
    ",1ll*res1*res2%mod);
    }
    int main(){
        scanf("%d%d%d",&n,&k,&m);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        if(m==0)cas1();
        else cas2();
        return 0;
    }
    

    C-分班问题

    题解

    标签:组合数+数学推导化简+Lucas定理

    这道题官方题解写的非常详细,这里完整过程就不赘述了。讲一下主要的解题过程

    一、40%数据((n,m,T<=2000))的暴力做法根据题意很好推出:

    for(int i=1;i<=min(n,m);i++)ans+=C(n,i)*C(m,i)*2*i;
    

    组合数可以在(O(N^2))时间内预处理出,每个询问可以在(O(N))的时间内完成。

    二、60%数据((T<=1e5,n,m<=1e6))做法可以根据通过数学化简上面那个(O(N))循环:

    ans=2*m*C(n+m-1,n-1)
    

    先在(O(N))时间内预处理出(1e6)范围内的模逆元和阶乘,然后询问就可以在(O(1))时间内解决了。但现在的瓶颈在于(O(N))预处理,所以下面考虑使用Lucas定理

    三、100%数据((T<=1e5,n,m<=1e18))

    发现(n,m)过大按常规方法无法求得组合数,并且题目给定的模数为质数,所以可以使用(Lucas定理)解决,关于Lucas定理

    #include<bits/stdc++.h>
    #define mod 19260817
    using namespace std;
    typedef long long ll;
    const int N=mod+10;
    int mny[N],fac[N];
    inline ll read(){
        ll x=0;char c=getchar();
        while(c<'0'||c>'9')c=getchar();
        while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
        return x;
    }
    int ksm(int x,int d){
    	int res=1;
    	while(d){
    		if(d&1)res=1ll*res*x%mod;
    		x=1ll*x*x%mod;d>>=1;
    	}
    	return res;
    }
    void init(){//预处理阶乘fac,模逆元mny 
    	fac[0]=1;
    	for(int i=1;i<mod;i++)fac[i]=1ll*fac[i-1]*i%mod;
    	mny[mod-1]=ksm(fac[mod-1],mod-2);
    	for(int i=mod-2;i>=0;i--)mny[i]=1ll*mny[i+1]*(i+1)%mod;
    }
    int C(ll a,ll b){//Lucas
    	if(a<b)return 0;
    	if(a>=mod)return 1ll*C(a%mod,b%mod)*C(a/mod,b/mod)%mod;
    	return (1ll*fac[a]*mny[a-b]%mod)*mny[b]%mod;
    }
    int main(){
    	init();
    	int T=read();
    	while(T--){
    		ll n=read(),m=read();
    		printf("%d
    ",1ll*2*m%mod*C(n+m-1,n-1)%mod);
    	}
    }
    

    最后bb一句:这是牛客最后一场了,第2,4,5场由于题目质量不佳没时间写就空着了qwq

  • 相关阅读:
    一款可以下拉搜索html下拉框控件
    Springboot+JPA+Thymeleaf 校园博客完整小网站
    OAuth 2.0 认证的原理与实践
    BootStrap 专题
    Rest接口和Thymeleaf的两个坑
    Android属性动画PropertyAnimation LayoutTransition(布局容器动画)
    Android 5.0中使用JobScheduler
    遍历Map的四种方法
    Android Error:(1,N1) 错误: 需要class, interface或enum
    Android.app.SuperNotCalledException错误
  • 原文地址:https://www.cnblogs.com/Tieechal/p/11420461.html
Copyright © 2011-2022 走看看