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;
    }
    		
    

      

     

  • 相关阅读:
    django页面分类和继承
    django前端从数据库获取请求参数
    pycharm配置django工程
    django 应用各个py文件代码
    CF. 1428G2. Lucky Numbers(背包DP 二进制优化 贪心)
    HDU. 6566. The Hanged Man(树形背包DP DFS序 重链剖分)
    小米邀请赛 决赛. B. Rikka with Maximum Segment Sum(分治 决策单调性)
    区间树 学习笔记
    CF GYM. 102861M. Machine Gun(主席树)
    2016-2017 ACM-ICPC East Central North America Regional Contest (ECNA 2016) (B, D, G, H)
  • 原文地址:https://www.cnblogs.com/Slager-Z/p/9350350.html
Copyright © 2011-2022 走看看