• CF Round #679 div2赛后总结


    前言

    好不容易遇到一次简单的div2,竟然才A了三题,可恶的第4题,死活调不出来QAQ。

    比赛地址

    A

    题意:给你(T)组数据,每组数据(n)个整数((n)是偶数),分别为(a_{1},a_2,...,a_n),每个数字的绝对值都小于等于(100)且不为(0)

    现在让你求一个长度为(n)(b)数组,满足每个数字是整数、绝对值都小于等于(100)且不为(0)

    题解:很简单啊,对于每个相邻的数字这样处理就行了:(a[1]*a[2]+(-a[1])*a[2]=0),所以(b[1]=a[2],b[2]=-a[1]),其余类似处理即可。

    时间复杂度:(O(n))

    非常SB的我还想了几分钟

    #include<cstdio>
    #include<cstring>
    using  namespace  std;
    int  n,a[110];
    int  main()
    {
    	int  T;scanf("%d",&T);
    	for(int  i=1;i<=T;i++)
    	{
    		scanf("%d",&n);
    		for(int  i=1;i<=n;i++)scanf("%d",&a[i]);
    		for(int  i=1;i<=n;i+=2)
    		{
    			int  y=i+1;
    			printf("%d %d ",-a[y],a[i]);
    		}
    		printf("
    ");
    	}
    	return  0;
    }
    

    B

    题意:T组数据,每组数据有个(n,m),表示(nm)的矩阵(满足每组数据的(n,m)加起来小于等于(250000)),然后序号为(1)~(nm)的点在这个矩阵中,然后其会给你每一行从左到右的点的编号,和每一列从上到下的点的编号,但是行与行、列与列之间的相对位置不一定是对的,现在要求你还原这个矩阵。

    题解:非常的简单,只要找到包含每一行第一个数字的列,就能得到行的相对位置,直接输出即可,时间复杂度可以到:(O(nm)),但是为了偷懒,我用排序快速的打出了(O(nmlog{nm}))的打法,虽然慢,但是打的快。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define  N  510
    #define  NN  260000
    using  namespace  std;
    struct  node
    {
    	int  a[N];
    }a[N];
    int  id[NN];bool  v[NN];
    inline  bool  cmp(node  x,node  y){return  id[x.a[1]]<id[y.a[1]];}
    int  n,m;
    int  main()
    {
    	int  T;scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d%d",&n,&m);
    		for(int  i=1;i<=n;i++)
    		{
    			for(int  j=1;j<=m;j++)scanf("%d",&a[i].a[j]);
    			v[a[i].a[1]]=1;
    		}
    		for(int  i=1;i<=m;i++)
    		{
    			int  x=0;
    			for(int  j=1;j<=n;j++)
    			{
    				scanf("%d",&x);
    				if(v[x]==1)id[x]=j;
    			}
    		}
    		sort(a+1,a+n+1,cmp);
    		for(int  i=1;i<=n;i++)
    		{
    			for(int  j=1;j<=m;j++)printf("%d ",a[i].a[j]);
    			printf("
    ");
    		}
    		for(int  i=1;i<=n;i++)v[a[i].a[1]]=0;
    	}
    	return  0;
    }
    

    C

    题意:现在有(6)个正整数的(a)数组,还有(n)个正整数的(b)数组(对于任意的(1≤i≤n,1≤j≤6),满足(b_{i}>a_{j})),然后要求现在构造一个(c)数组,对于(c_{i}),其等于(b_{i}-a{j})(j)是自己定的),然后(c)数组的权值为最大的数字减去最小的数字,求最小的权值。

    题解:我们不妨考虑暴力枚举(l)虽然是1e9的级别,然后看看其对应的(r)最小能是多少,这个应该怎么维护呢?也就是说([l,r])中必须能包含一个(c)数组。

    我们不妨用一个数字把每一个(b_{i}-a_{j})保存起来,总共(6n)个数字,从小到大排序,然后对于(l++),我们只要把所有(b_{i}-a_{j}<l)删掉,然后找到另外一个最小的(b_{i}-a_{k}≥l)加入进去即可,然后(r)(max)

    但是(l)移动(1e9)次的问题还有解决,我们发现,(l)只有移动到(6n)个数字才是有用的,于是优化一下,(l)就只用跳(6n)次了,而每个数字最多被删除一次,也是(6n)次,所以就是(O(nlogn))。(实际上用基排可以到(O(n))

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define  N  110000
    #define  NN  610000
    using  namespace  std;
    inline  int  mymax(int  x,int  y){return  x>y?x:y;}
    inline  int  mymin(int  x,int  y){return  x<y?x:y;}
    int  a[10],b[N],n;
    struct  node
    {
    	int  x/*数字*/,y/*对应的哪个b[y]*/,next/*下一个b[y]-a[k]*/;
    }dp[NN];int  las[N],len;
    inline  bool  cmp(node  x,node  y){return  x.x<y.x;}
    inline  bool  cmp2(int  x,int  y){return  x>y;}
    int  main()
    {
    	memset(las,0,sizeof(las));
    	for(int  i=1;i<=6;i++)scanf("%d",&a[i]);
    	sort(a+1,a+7,cmp2);//其实没有必要
    	
    	scanf("%d",&n);
    	int  l,r=1;
    	for(int  i=1;i<=n;i++)
    	{
    		scanf("%d",&b[i]);
    		r=mymax(r,b[i]-a[1]);
    		for(int  j=1;j<=6;j++)
    		{
    			len++;
    			dp[len].x=b[i]-a[j];
    			dp[len].y=i;
    		}
    	}
    	sort(dp+1,dp+len+1,cmp);
    	
    	for(int  i=len;i>=1;i--)//处理next
    	{
    		dp[i].next=las[dp[i].y];
    		las[dp[i].y]=i;
    	}
    	l=dp[1].x;int  ans=1000000000;
    	for(int  i=1;i<=len;)
    	{
    		ans=mymin(r-l,ans);
    		while(i<=len  &&  dp[i].x==l)//不断的删除数字
    		{
    			if(!dp[i].next)//已经没有数字了
    			{
    				printf("%d
    ",ans);
    				return  0;
    			}
    			r=mymax(dp[dp[i].next].x,r);
    			i++;
    		}
    		l=dp[i].x;
    	}
    	printf("%d
    ",ans);
    	return  0;
    }
    

    D

    题意:一个人要卖(1)~(n)价格的物品(每个物品各一个),每个时间点有两种操作:

    1. 其放上一个物品,价格不知道。
    2. 一个人来买走最小价格的物品。

    现在给你(2n)个时间点的操作,(+)表示放上物品,(-) (x)表示一个人来买走了(x)价格的物品,现在要求你构造出一个满足要求的放物品序列,没有输出(NO)

    题解:设(a[i])为第(i)次买走是什么物品,(id[i])满足(∀j∈[id[i],i-1],a[j]<a[i]),且要求(id[i])是最小的,而对于每个(+)号,其隶属于后面第一个(-)号,很明显,(a[i])只要放在隶属于([id[i],i])中的任意一个(+)号,而且不难发现,(a[i])放在([1,id[i]-1])的位置会导致(id[i]-1)错误,所以(a[i])只能且任意放在隶属于([id[i],i])中的任意一个(+)号。

    当然,不难发现,对于每个数字,在前面已经放完之后,尽量的往前方就行了,用并查集维护。(往后放可能会导致后面的数字放不了)

    时间复杂度:(O(nlogn))

    #include<cstdio>
    #include<cstring>
    #define  N  110000
    #define  NN  210000
    using  namespace  std;
    int  fa[N];
    int  findfa(int  x)
    {
    	if(fa[x]!=x)fa[x]=findfa(fa[x]);
    	return  fa[x];
    }
    int  ll[N],rr[N],a[N];
    int  id[N],n;
    int  sta[N],top;
    inline  int  erfen(int  x)
    {
    	int  l=1,r=top,ans=x,mid;
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(a[sta[mid]]<a[x])r=mid-1,ans=sta[mid-1]+1;
    		else  l=mid+1;
    	}
    	return  ans;
    }
    int  lis[N]; 
    int  main()
    {
    //	freopen("std.in","r",stdin);
    //	freopen("std.out","w",stdout);
    	scanf("%d",&n);
    	int  ed=2*n;
    	int  l1=0,l2=0,pre=1;
    	for(int  i=1;i<=ed;i++)
    	{
    		char  st[10];
    		scanf("%s",st+1);
    		if(st[1]=='-')
    		{
    			l1++;scanf("%d",&a[l1]);
    			ll[l1]=pre;rr[l1]=l2;pre=l2+1;
    			fa[l1]=l1;
    		}
    		else  l2++;
    	}
    	fa[n+1]=n+1;
    	
    	id[1]=1;sta[top=1]=1;
    	for(int  i=2;i<=n;i++)
    	{
    		id[i]=erfen(i);
    		while(top  &&  a[sta[top]]<a[i])top--;
    		sta[++top]=i;
    	}
    	
    	for(int  i=1;i<=n;i++)
    	{
    		int  x=findfa(ll[id[i]]);
    		if(x>rr[i])
    		{
    			printf("NO
    ");
    			return  0;
    		}
    		else
    		{
    			lis[x]=a[i];
    			fa[x]=x+1;
    		}
    	}
    	printf("YES
    ");
    	for(int  i=1;i<=n;i++)printf("%d ",lis[i]);
    	printf("
    ");
    	return  0;
    }
    
    

    事实上,二分部分可以跟单调栈的弹出合并到一起,并查急可以优化到(O(nα(n)))

    所以可以到达(O(nα(n)))

    当然,还有严格(O(n))的做法,不难发现,我们的瓶颈在于往后放可能会导致后面的数字放不了,但是我们发现,如果(a[j]<a[j](i<j)),那么(a[i])能放到的地方(a[j])也能放到,所以(a[i])往后放,而不是往前放的话,并不会影响(a[j])放置,如果(a[j]>a[i])(a[i])压根就放不到(a[j])能放的位置,(a[j])不就随便放了吗?

    然后用单调栈随便维护一下就可以了。

    时间复杂度:(O(n))

    #include<cstdio>
    #include<cstring> 
    #define  N  110000
    #define  NN  210000
    using  namespace  std;
    inline  int  mymin(int  x,int  y){return  x<y?x:y;}
    inline  int  mymax(int  x,int  y){return  x>y?x:y;}
    int  sta[NN],top;
    int  n,lis[N];
    int  main()
    {
    	scanf("%d",&n);
    	int  ed=2*n,l1=0;
    	for(int  i=1;i<=ed;i++)
    	{
    		char  st[10];scanf("%s",st);
    		if(st[0]=='+')l1++,sta[++top]=-l1;
    		else
    		{
    			int  x=0;scanf("%d",&x);
    			bool  bk=0;
    			while(top)
    			{
    				if(sta[top]>0  &&  sta[top]<x)top--;
    				else  if(sta[top]<0)
    				{
    					lis[-sta[top]]=x;
    					top--;
    					bk=1;
    					break;
    				}
    				else  break;
    			}
    			if(!bk)
    			{
    				printf("NO
    ");
    				return  0;
    			}
    			sta[++top]=x;
    		}
    	}
    	printf("YES
    ");
    	for(int  i=1;i<=n;i++)printf("%d ",lis[i]);
    	printf("
    ");
    	return  0;
    }
    

    E

    题意:有一个魔法,在(t)秒施法时瞬间打掉怪物(a)点血量,然后在(t+1,t+2,...,t+c)的时间点回复(b)点血量,施法有(d)(cd),相当于(t)秒施法后,(t+d)才能再次施法。

    回血效果可以叠加,如果一个时间点有多个血量变化,同时计算,然后问你最多可以打掉多少血的怪物,无限的话输出(-1)

    (T)组数据,每组给定魔法(a,b,c,d)

    做法:不难发现,(a>bc)的话,就是(-1),反之,不能打败无限血的怪物,那么很明显,(t)时刻放完魔法后,(t+d)时刻立马放很明显更加优秀。(你总不可能等他多回一点血再打吧。)

    所以放魔法的时间就是:(1,1+d,1+2d,1+3d...),那么什么时候达到最大值呢?

    考虑回本时间,回本时间就是指(1)时刻放完魔法后,在哪个时刻第一次的攻击血量会被其回血血量会上来,不难发现,会本时间就是:((b-1)/a+2),设为(tim)

    先证明(≥tim)再放魔法的话肯定不是最大值:
    对于(k≥tim)放完魔法,此时(1)时刻的攻击已经被完全的消除了(甚至可能回得更多),那么不妨构造新的方案,在(1)时刻不攻击,(1+d)时刻为真正的(1)时刻,此时最大的攻击血量一定不小于原来的方案。

    再证明(<tim)放魔法一定会更大:
    类似的证明方法,在(k<tim)的位置实施魔法,不妨证明其比(1,1+d,...,k-d)的方案更加优秀,类似的证明,构造新方案,(1)不放魔法,(1+d)为名正言顺的(1)时刻,那么新方案的血量等于(1,1+d,...,k-d)的方案,但是原方案比新方案多了个(1),且其并未回本,所以原方案(>)新方案(=)(1,1+d,...,k-d)的方案。

    然后推推式子就行了。

    单次复杂度:(O(1))

    #include<cstdio>
    #include<cstring>
    using  namespace  std;
    typedef  long  long  LL;
    int  main()
    {
    	int  T;scanf("%d",&T);
    	while(T--)
    	{
    		LL  a,b,c,d;scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
    		if(a>b*c)printf("-1
    ");
    		else
    		{
    			LL  tim=(a-1)/b+1;//回本时间-1
    			LL  kao=(tim-1)/d+1;
    			
    			LL  ans=kao*a-(d*b)*kao*(kao-1)/2;
    			printf("%lld
    ",ans);
    		}
    	}
    	return  0;
    }
    
  • 相关阅读:
    Django内置Admin解析
    python项目 配置文件 的设置
    Django---信号
    bash配置文件
    week4 作业
    shell基础练习题
    shell基础
    shell变量与运算
    week3 作业
    文件权限管理
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/13877088.html
Copyright © 2020-2023  润新知