zoukankan      html  css  js  c++  java
  • qbxt Day 1

    Day 1

    考试题解

    T1 打扑克

    唯一切掉的题,终于签到成功了。本题其实非常简单,打斗地主打多了的人真的是一眼秒。一看数据1000位,看都不用看就是找规律。再看分成奇数偶数两组,就想到通过奇偶性来分类讨论一波。手玩几组小数据,很容易发现规律:一共就四种情况:1.共奇数张牌,奇数先出。2.共奇数张牌,偶数先出。3.共偶数张牌,偶数先出。4.共偶数张牌,奇数先出。自己推导一下就能发现,只有总数是奇数并且奇数先出奇数才能赢,其他都会输,所以直接判字符串末位的奇偶性即可。但是有一种特殊情况,就是(n)(2)的时候是谁先出谁赢,本题共有80分有这个点。。。。。所以说很多人没有考虑到就挂了(80),还是要注意细节。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    	return x*f;
    }
    int T,op;
    char n[1010];
    int main()
    {
    	T=read();
    	while(T--){
    		cin>>n;
    		op=read();
    		if(strlen(n)==1&&n[0]=='2'){
    			if(op==0){
    				printf("0
    ");
    				continue;
    			}
    			else{
    				printf("1
    ");
    				continue;
    			}
    		}
    		if((n[strlen(n)-1]-'0')%2==1){//奇数张牌 
    			if(op==0){//
    				printf("0
    ");
    			}
    			else{
    				printf("1
    ");
    			}
    		}
    		else{
    			if(op==0){
    				printf("1
    ");
    			}
    			else{
    				printf("1
    ");
    			}
    		}
    	}
    	return 0;
    }
    

    T2 粉刷匠

    考试时觉得是道可做题,推容斥能够推出来。先写了个暴力(O(k(m+n))),拿到(30)。但结果就是我越推越乱越推越乱,从没跟暴力拍上过,做了大约一个小时有点自闭,然后去做(T3)了。打完(T3)暴力,看了眼(T4),觉得不可做就又回来做(T2)。然后一直推到最后遗憾离场,(30pts)滚粗。中午回去巨神(CYC)告诉可以倒着做,因为倒着做就不用再考虑会不会被覆盖了。我恍然大悟,然后回来切掉了这道题。

    其实这道题在某些方面跟去年江西(CSP-S)(T3)网格图中间求和的那部分非常非常相似,如果一行被染成了蓝色,那么就看看有几列已经被确定了,减去这几列即可。染成红色的话只需要去掉对答案的贡献这一行代码即可。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef long long ll; 
    inline ll read(){
    	ll x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    	return x*f;
    }
    ll n,m,k,ans,lsum,hsum;
    ll x[1000005],y[1000005],z[1000005];
    bool hang[1000005],lie[1000005];
    int main()
    {
    	scanf("%lld%lld%lld",&n,&m,&k);
    	for(ll i=1;i<=k;i++){
    		scanf("%lld%lld%lld",&x[i],&y[i],&z[i]);
    	}
    	for(ll i=k;i>=1;i--){
    		if(z[i]==1){
    			if(x[i]==0){
    				if(!hang[y[i]]){
    					hang[y[i]]=1;
    					ans+=(m-lsum);
    					hsum++;
    				}
    			}
    			else{
    				if(!lie[y[i]]){
    					lie[y[i]]=1;
    					ans+=(n-hsum);
    					lsum++;
    				}
    				
    			}
    		}
    		else{
    			if(x[i]==0){
    				if(!hang[y[i]]){
    					hang[y[i]]=1;
    					hsum++;
    				}
    			}
    			else{
    				if(!lie[y[i]]){
    					lie[y[i]]=1;
    					lsum++;
    				}
    			}
    		}
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    //  不开longlong见祖宗 
    

    拓展:疯狂的馒头

    T3 直线竞速

    首先一眼看出了(70pts)的做法,只需要暴力算出查询的每一秒的时候每一个人的位置然后排序即可,时间复杂度是(O(Qnlogn)),这样能得到(30pts)。但发现还有(40pts)是只求第一名,那么在算每一个人的位置时直接在循环中更新最大值即可,时间复杂度是(O(Qn))。就得到了(70pts)

    但是令我万万没想到的是,我离正解只差了一个(STL)函数!!!在神奇的(STL)库里,有一个神奇的函数叫做(nth_element())。那么这个函数的意思是什么呢:在O(n)的时间复杂度内,在无序序列中,找到第(k)小的数。

    我:???????

    然后用了函数之后总时间复杂度就到了(O(Qn)),可以切掉这道题。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    	return x*f;
    }
    int n,Q,t,k,num1;
    int v[7010],a[7010];
    long long maxx;
    struct F{
    	int num;
    	long long s;
    }f[7010];
    long long cmp(F A,F B){
    	return A.s>B.s;
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;i++){
    		v[i]=read();
    		a[i]=read();
    	}
    	Q=read();
    	for(int i=1;i<=Q;i++){
    		t=read();
    		k=read();
    		maxx=0;
    		for(int j=1;j<=n;j++){
    			f[j].s=a[j]+1ll*v[j]*t;
    			f[j].num=j;
    			if(f[j].s>maxx){
    				maxx=f[j].s;
    				num1=f[j].num;
    			}
    		}
    		if(k==1){
    			printf("%d
    ",num1);
    			continue;
    		}
    		nth_element(f+1,f+k,f+1+n,cmp);//并不知道咋用,瞎搞一波然后对了
    		printf("%d
    ",f[k].num);
    	}
    	return 0;
    }
    
    

    但是老师的正解似乎并不是这样。。。(这做法确实nc而且low)

    老师承认这题忘记了有这个函数,出水了,如果手写分治拿到满分也可以接受,但是(STL)就过分简单了。

    下面说一下分治求第(k)小元素的原理:随便找一个元素放在中间当作挡板,分成两半,将小于这个数的元素放在左边,反之放在右边。然后看看左边数的个数和右边数的个数。如果左边的数的个数不到(k),那么数肯定在右边,就相当于求右边序列中的第(k-x)(左边有(x)个数)小的数,递归求解即可,复杂度是(O(n))

    正解做法2:

    在起点处排序,每个选手之间的相对位置不可能跨越交换,只能相邻两人直接交换。这与冒泡排序的思想类似,因为速度快的超过速度慢的之后就不会再被慢的人超过,所以最多交换(n^2)次。就相当于每次交换减少一对逆序对。这不就是冒泡排序?!所以做法就是先提前将询问的时间排序,求出每个人那一秒在多远的位置,然后将所有的逆序对交换,然后输出排名。由于交换后不会再换回来,所以下一次询问在当前序列基础上继续操作即可。由于最多产生(n^2)个逆序对,所以最多交换(n^2)次,总时间复杂度是(O(n^2+QlogQ+nQ))

    T4 游戏

    ?分做法:

    (f_{i,j})表示(A)的前(i)个数和(B)的前(j)个数做游戏的最小总花费。(f_{i,j}=min(f_{k,r}+(SA_i-SB_k)*(SA_j-SA_r),0<=k<i,0<=r<j)(这式子没记全QWQ)。(SA)(SB)表示(A)(B)的前缀和。总时间复杂度:(O(n^2m^2))

    100分做法:

    发现一结论:每次取的两端中至少有一段中只有一个数。假设(A)序列我分成两段取,两段的值分别是(x),(y)(B)序列也如此,分为(a),(b)两段。那么合起来的贡献就是((x+y)*(a+b))要比分开的贡献((ax+by))大,所以显然分开更优。就这样一直分下去,发现至少有一个序列在每一次操作的时候只选一个数最优,那么枚举是哪一段最后包含那一个只有一个数的那一段即可。用(f_{i_j})来记录(A)序列选(i)个数然后(B)序列选(j)个数的时候最小贡献是多少。那么考虑转移方程:当两个序列都只选一个数的时候,那么很明显,就是由各自长度-1然后加上当前两个数的乘积。但如果不是的话,则可以看做是此过程重复多次。比如说(A)序列取一个数,然后(B)序列取了任意多个数。则可以看做(A)序列没有删掉最后一个数,然后一直乘下去直到乘完这些数,那么转移就是(f_{i,j}=f_{i,j-1}+a_i*b_j)。同理,就可以得到三个不同状态,然后取最小值,即是答案。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    using namespace std;
    typedef long long int ll;
    int n,m;
    int a[2010],b[2010]; 
    ll f[2010][2010];
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%d",a+i);
    		a[i]--;
    	}
    	for(int i=1;i<=m;i++){
    		scanf("%d",b+i);
    		b[i]--;
    	}
    	memset(f,0x3f,sizeof(f));//初始化为最大值
    	f[0][0]=0;//什么都没选贡献当然是0
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			ll x=a[i]*b[j];
    			f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+x;//三个不同的状态
    		}
    	}
    	printf("%lld
    ",f[n][m]);
    	return 0;
    }
    

    DFS

    NOI 1999 生日蛋糕

    常规操作:如果已经大于(ans)直接(return)

    最优性剪枝:(v=pi*r^2*h,s=2*pi*r*h,s=2*v/r),所以当体积一定时,r最大的时候最优。然后将半径带入体积求出高度继续搜索。

    可行性剪枝:如果余下的蛋糕圆柱体不能满足半径和高都小于当前蛋糕圆柱体(当前圆柱体过小)。如果叠最小的圆柱体都会超出体积,那么也剪掉。

    BFS

    HAOI 2008 移动玩具

    把所有边建出来然后(BFS)

    拓扑序明显的时候用(dp),常数小。

  • 相关阅读:
    几个有趣的在线设计网站搜集
    动物水池png+ico图标:pool
    看看3150亿美元叠起来有多高
    卡巴斯基免费key发布网站:kaspersky
    58个精品壁纸分享网站[精品]
    一套华丽的vista安装包png+ico图标:vistalike
    32个精品国外图文结合photoshop效果教程
    超酷背景笔刷[abr格式]:grunge
    针对MM的在线处理图片网站:Taaz
    如何设计web2.0的图标[两个photoshop web2.0样式asl下载]
  • 原文地址:https://www.cnblogs.com/57xmz/p/13759454.html
Copyright © 2011-2022 走看看