zoukankan      html  css  js  c++  java
  • 小X归来 模拟赛1 解析

     Problem1 单峰

     X 归来后,首先对数列很感兴趣。他想起有1类特殊的数列叫单峰数列。 我们说一个数列 {ai} 是单峰的,当且仅当存在一个位置 k 使得 ai < ai+1(i < k) ai > ai+1(i k)。 现在小X 想知道,对于 1 n 的所有排列,其中有多少个是单峰数列。 Input 第1行包含1个整数 nOutput 第一行包含一个整数,表示答案除以 1e9 + 7 的余数。 Example

    unimodal.in unimodal.out
    2 2

    Scoring 对于 20% 的数据, n 10对于 50% 的数据, n 105对于 100% 的数据, 2 n 1018

     解析:

    1.1 20 分做法

    生成所有全排列并判断,时间复杂度 O(n · n!)
    1.2 50 分做法
    根据排列组合可以发现,峰顶一定是 n,因此考虑 1 n - 1 分别放在 n 的左边还是右边,一一得出相应
    的唯一方案。所以答案就是 2^(n-1),时间复杂度 O(n)
    1.3 100 分做法
     对2^(n-1)用快速幂即可,时间复杂度 O(log n)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    template <class T>
    inline void readl(T &x)
    {	x=0;bool f=0;char ch=getchar();
    	while(!isdigit(ch)) {	f=(ch==45);ch=getchar();}
    	while( isdigit(ch)) {	x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	x=f?(~x+1):x;}
    long long n;
    const int md=1e9+7;
    inline long long bpow(long long  a,long long b)
    {  long long  base=a;
    	long long ans=1;
        while(b)
        {
            if(b&1) ans=(ans%md*base%md)%md;
            base=base*base%md;
            b>>=1;
            }
    	return ans%md;
        }
    int main()
    {	freopen("unimodal.in","r",stdin);
    	freopen("unimodal.out","w",stdout);
    	readl(n);
    	printf("%lld
    ",(bpow(2,n-1)%md));
    	return 0;
    	}
    

    Problem2 积木

    X 感到很无聊,从柜子里翻出了他小时候玩的积木, 这套积木共有 n 块,每块积木都是1个长方体。小X 想用这些积木拼成1个积木塔(不必每块 积木都使用, 所谓积木塔,就是将积木1个1个摞起来,(除去最底层的积木外)每块积木的底下必须能被它下面 的积木的底面完全包含(即对应的长宽都要更小或相等)。当然,积木可以任意放置,即可以以任意一面 作为底面。 现在小X 想知道,积木塔最大能拼多高。 Input 第⼀⾏包含⼀个整数 n。 接下来 n ⾏,每⾏包含三个整数 a; b; c,表⽰该块积木是⼀个 a × b × c 的长⽅体。 Output 第⼀⾏包含⼀个整数,表⽰答案。 Example

    brick.in brick.out

    3

    8 7 6

    3 9 4

    1 10 5

    18

    Explanation 选择第 1 块积木和第 3 块积木。

    Scoring   对于 10% 的数据, n = 1

        对于 40% 的数据, n 6

        • 对于 100% 的数据, 1 n 151 a; b; c 108

    解析:

    2.1 10 分做法
    输出 maxfa; b; cg
    2.2 40 分做法
    生成全排列,然后枚举每个积木哪个面朝上,时间复杂度 O(n! · 3n)
    2.3 100 分做法
    显然是状态压缩 DP。设计状态 f[S][i][0/1/2] 表示已经用了集合 S 内的积木,最顶上是编
    号为 i 的积木,它的哪个面朝上。转移时枚举不在 S 内的积木,以及朝上的面判断即可。时间
    复杂度 O(2n · (3n)2)

    PS:但是这道题的数据有点弱,只要搜索写得好,照样可以过。

    代码:

    1.DP

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int readl()
    {
    	int x=0,fg=1;char ch=getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')fg=-fg;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return x*fg;
    }
    int n,a[200],b[200],c[200],t[20],mx=-100,bin[200];
    ll ans=-100;
    ll f[(1<<15)+10][16][3];
    int main()
    {
    	freopen("brick.in","r",stdin);
    	freopen("brick.out","w",stdout);
    	n=readl();
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=readl();b[i]=readl();c[i]=readl();
    		t[1]=a[i];t[2]=b[i];t[3]=c[i];
    		sort(t+1,t+4);
    		a[i]=t[1];b[i]=t[2];c[i]=t[3];
    		mx=max(mx,t[3]);
    	}
    	if(n==1){printf("%d",mx);return 0;}
    	bin[1]=1;
    	for(int i=2;i<=20;i++)bin[i]=bin[i-1]*2;
    	for(int i=1;i<=n;i++)
    	{
    		f[bin[i]][i][0]=a[i];
    		f[bin[i]][i][1]=b[i];
    		f[bin[i]][i][2]=c[i];
    	}
    	//printf("%lld %lld %lld
    ",f[1][1][0],f[1][1][1],f[1][1][2]);
    	for(int x=0;x<(1<<n);x++)
    		for(int i=1;i<=n;i++)
    		{	if(x&bin[i])continue;
    			for(int j=1;j<=n;j++)
    			{
    				if((x&bin[j])==0)continue;
    				int s=x|bin[i];
    				if(b[j]>=b[i]&&c[j]>=c[i])f[s][i][0]=max(f[x][j][0]+a[i],f[s][i][0]);
    				if(c[j]>=c[i]&&a[j]>=b[i])f[s][i][0]=max(f[s][i][0],f[x][j][1]+a[i]);
    				if(b[j]>=c[i]&&a[j]>=b[i])f[s][i][0]=max(f[s][i][0],f[x][j][2]+a[i]);
    					
    				if(c[j]>=c[i]&&b[j]>=a[i])f[s][i][1]=max(f[s][i][1],f[x][j][0]+b[i]);
    				if(c[j]>=c[i]&&a[j]>=a[i])f[s][i][1]=max(f[s][i][1],f[x][j][1]+b[i]);
    				if(b[j]>=c[i]&&a[j]>=a[i])f[s][i][1]=max(f[s][i][1],f[x][j][2]+b[i]);
    				
    				if(c[j]>=b[i]&&b[j]>=a[i])f[s][i][2]=max(f[s][i][2],f[x][j][0]+c[i]);
    				if(c[j]>=b[i]&&a[j]>=a[i])f[s][i][2]=max(f[s][i][2],f[x][j][1]+c[i]);
    				if(b[j]>=b[i]&&a[j]>=a[i])f[s][i][2]=max(f[s][i][2],f[x][j][2]+c[i]);
    				ans=max(ans,max(f[s][i][0],max(f[s][i][1],f[s][i][2])));
    				//printf("%d %d %d %d ***
    ",x,s,i,j);
    				//printf("%lld %lld %lld
    ",f[s][i][0],f[s][i][1],f[s][i][2]);
    			}
    		}
    	printf("%lld",ans);
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    

     2.DFS

    #include<bits/stdc++.h>
    using namespace std;
    template <class T>
    inline void readl(T &x)
    {	x=0;bool f=0;char ch=getchar();
    	while(!isdigit(ch)) {	f=(ch==45);ch=getchar();}
    	while( isdigit(ch)) {	x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	x=f?(~x+1):x;}
    
    int n,maxn=0;
    bool vis[1001];
    struct node
    {	int mx,h[4],x[4],y[4];}e[100];
    void dfs(int H,int x,int y)
    {	maxn=max(maxn,H);
        for(register int i=n;i>=1;i--)
            if(!vis[i])
    		{	vis[i]=1;
                for(register int j=1;j<=3;j++)
                    if(e[i].x[j]<=x&&e[i].y[j]<=y)
                        dfs(H+e[i].h[j],e[i].x[j],e[i].y[j]);
                vis[i]=0;
    			}
    	}
    int main()
    {	freopen("brick.in","r",stdin);
    	freopen("brick.out","w",stdout);
        readl(n);
        for(register int i=1,x,y,z;i<=n;i++)
    	{	readl(x);readl(y);readl(z);
            e[i].mx=max(x,max(y,z));
            e[i].x[1]=min(x,y);e[i].y[1]=max(x,y);e[i].h[1]=z;
            e[i].x[2]=min(y,z);e[i].y[2]=max(y,z);e[i].h[2]=x;
            e[i].x[3]=min(x,z);e[i].y[3]=max(x,z);e[i].h[3]=y;
    		}
        if(n==1){printf("%d",e[1].mx);return 0;}
        for(int i=1;i<=n;i++)
    	{	vis[i]=1;
            for(int j=1;j<=3;j++)
                dfs(e[i].h[j],e[i].x[j],e[i].y[j]);
            vis[i]=0;
    		}
        printf("%d",maxn);
        return 0;
    	}
    

    Problem3 同余

    小X 望着草稿纸上的数列,结合自己对同余的粗浅认识,又想到了个新问题。 对于1个长度为 n 的数列 {ai},每次询问将给出1组数 l; r; p; q,小X 想知道有多少个 i 满足 l i r ai q (mod p)。 小X 沉迷这个问题,因此一共进行了 m 次询问。 Input 第⼀⾏包含两个整数 n; m。 第⼆⾏包含 n 个整数,表⽰数列 faig。 接下来 m ⾏,每⾏包含四个整数 l; r; p; q,表⽰⼀次询问。 Output m ⾏,每⾏包含⼀个整数,表⽰该次询问的答案。 Example

    congruence.in congruence.out

    5 2

    1 5 2 3 7

    1 3 2 1

    2 5 3 0

    2

    1

    Scoring   对于 20% 的数据, ai 1

         对于 60% 的数据, ai 100

         对于 100% 的数据, 1 m 1051 l r n 1050 q < p 100000 ai 10000

    解析:

    3.1 20 分做法
    ai 6 1,因此转变为求区间内 0 1 的个数,用前缀和优化,注意 p = 1 的情况。时间复杂
    O(n + m)
    3.2 60 分做法
    同上面,用至多 101 个前缀和即可,时间复杂度 O(ai(n + m))
    3.3 100 分做法
    观察到,其实要求的是某一范围内 kp + q 的个数,当 p 较大时, k 的取值范围很小。不妨
    设“较大”的界限是 > K
    考虑将问题拆开来并排序,这样每个问题就变成了询问 1 r 中有多少个 kp + q。维护⼀
    个哈希数组, h[i] 表示 i 有多少个;以及⼀个模数数组 g[i][j],表示模 i j 有多少个。
    每次指针向右移,直到移动到当前询问的位置,每移一次就将这个数分别在两个数组内标
    记,复杂度总体是 O (nK)
    每次询问时,对于较小的 p 直接在 g 中查询,对于较⼤的 p 枚举 k 并在 h 中查询。
    可以看出 K = √ai = 100 时最优。

    代码:

    1.100分

    #include<bits/stdc++.h>
    using namespace std;
    template <class T>
    inline void readl(T &x)
    {	x=0;bool f=0;char ch=getchar();
    	while(!isdigit(ch)) {	f=(ch==45);ch=getchar();}
    	while( isdigit(ch)) {	x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	x=f?(~x+1):x;}//读入优化
    
    int n,m;
    int a[100100],sum[100100],ans[100100];
    struct range
    {	int r,id,p,q,opt;}e[100100<<1];//注意开两倍的空间
    int maxn=0;
    int cmp(range a,range b)
    {	return a.r<b.r;}
    int main()
    {	freopen("congruence.in","r",stdin);
    	freopen("congruence.out","w",stdout);
    	readl(n);readl(m);
    	for(int i=1;i<=n;i++)
    		readl(a[i]);
    	for(int i=1,l,r,p,q;i<=m;i++)
    	{	readl(l);readl(r);readl(p);readl(q);
    		e[i].r=l-1;  e[i].id=i;    e[i].p=p;     e[i].q=q;     e[i].opt=-1;//需要计算sum[r]-sum[l-1]
    		e[i+m].r=r;e[i+m].id=i;e[i+m].p=p;e[i+m].q=q;e[i+m].opt=1;//将左右两边分开计算,将无序的提问变为线性的区间计算
    		}
    	sort(e+1,e+1+2*m,cmp);
    	int tail=0;//解析中的指针
    	for(int i=1;i<=2*m;i++)
    	{	while(tail<e[i].r)
    		{	tail++;
    			sum[a[tail]]++;//将所有的数字出现的个数都统计一遍
    			maxn=max(maxn,a[tail]);//统计最大值,方便确定下文kp+q的界限
    			}
    		for(int j=0;j*e[i].p+e[i].q<=maxn;j++)//解析中的kp+q
    			ans[e[i].id]+=sum[j*e[i].p+e[i].q]*e[i].opt;//e[i].id表示询问的序号,如果opt=-1就代表左界限,乘积为负代表减去[1,l-1]的值,opt=1同理
    		}
    	for(int i=1;i<=m;i++)
    		printf("%d
    ",ans[i]);//输出答案
    	return 0;
    	}
    

     2.60分(重点是理解40%的前缀和写法)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int readl()
    {
    	int x=0,fg=1;char ch=getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')fg=-fg;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return x*fg;
    }
    int n,m,a[101000],s[101000],v[100010][101];
    int ac[101000];
    int calc(int l,int r,int p,int q)
    {
    	int ret=0;
    	for(int i=l;i<=r;i++)
    		if(a[i]%p==q%p)ret++;
    	return ret;
    }
    int main()
    {
    	freopen("congruence.in","r",stdin);
    	freopen("congruence.out","w",stdout);
    	n=readl();m=readl();
    	int bo=0;
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=readl();if(a[i]>1)bo=1;
    	}
    	int ans,l,r,p,q;
    	if(n<=5010&&m<=5010)
    	{	
    		for(int i=1;i<=m;i++)
    		{
    			l=readl();r=readl();p=readl();q=readl();
    			printf("%d
    ",calc(l,r,p,q));
    		}
    		return 0;
    	}
    	if(bo==0)
    	{
    		for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
    		for(int i=1;i<=m;i++)
    		{
    			l=readl();r=readl();p=readl();q=readl();
    			if(p==1){ans=r-l+1;printf("%d
    ",ans);continue;}
    			if(q%p==0)ans=r-l+1-s[r]+s[l-1];
    			if(q%p==1)ans=s[r]-s[l-1];
    			if(q%p>1)ans=0;
    			printf("%d
    ",ans);
    		}
    		return 0;
    	}
    	for(int i=1;i<=n;i++)//重点
    		for(int j=0;j<=100;j++)
    		{
    			v[i][j]=v[i-1][j];
    			if(a[i]==j)v[i][j]++;
    		}
    	for(int i=1;i<=m;i++)
    	{
    		l=readl();r=readl();p=readl();q=readl();ans=0;
    		if(p==1){ans=r-l+1;printf("%d
    ",ans);continue;}
    		ac[0]=0;
    		for(int i=0;i<=100;i++)
    		if(i%p==q%p)ac[++ac[0]]=i;
    		for(int i=1;i<=ac[0];i++)
    		ans=ans+v[r][ac[i]]-v[l-1][ac[i]];
    		printf("%d
    ",ans);
    	}
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    		
    

      

     

  • 相关阅读:
    Linux 修改时区(debain)
    C#生成验证码
    httpPost 请求
    C# Array.FindAll
    C#删除字符串中所有的空字符
    C# 获取汉字区位码
    Qinq Vlan mapping 和DHCP 服务器
    Sql 分页存储过程
    request 之getParameterMap 方法
    如果我是面试官
  • 原文地址:https://www.cnblogs.com/Slager-Z/p/9350350.html
Copyright © 2011-2022 走看看