• CF#680 Div.2赛后总结


    前言

    哭了啊,又被同机房那几个奆佬摁在地上摩擦,平均比每人少做一道题目。

    比赛链接:https://codeforc.es/contest/1445

    A

    题意:给你两个数组(a,b),让你判断能不能通过对(b)重新的排序,让其满足:(a_{i}+b_{i}≤k(1≤i≤n)),其中(k)是给定的常数。

    做法:不难发现,(a)升序,(b)降序,然后暴力做即可。

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

    #include<cstdio>
    #include<cstring>
    #define  N  110
    using  namespace  std;
    int  a[N],b[N],n,k;
    int  main()
    {
    	int  T;scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d%d",&n,&k);
    		for(int  i=1;i<=n;i++)scanf("%d",&a[i]);
    		for(int  i=1;i<=n;i++)scanf("%d",&b[i]);
    		bool  bk=0;
    		for(int  i=1;i<=n;i++)
    		{
    			if(a[i]+b[n-i+1]>k)
    			{
    				bk=1;
    				break;
    			}
    		}
    		if(!bk)printf("Yes
    ");
    		else  printf("No
    ");
    	}
    	return  0;
    }
    

    B

    题意: 一场比赛有不知道多少人(>100)个人参与,在淘汰赛有两场比赛,两场比赛的分数和总分数都按按降序排序,如果有同分按字典序排序,然后现在两场的分数排名给出来,但是不知道具体的分数,问你最后总排名第(100)名的分数最少能是多少。

    当然,两场比赛的分数还是知道一点信息的,第一场比赛第(100)名是(a)分,然后前(100)名在第二场比赛至少得了(b(b≤c))分,第二场比赛第(100)名是(c)分,前(100)名在第一场比赛至少得了(d(d≤a))分。

    做法:艹,题意就看了半天,贪心:首先为了第(100)名分数最小,肯定第一场比赛前(100)名都拿(a)分,然后这些人在第二场比赛都拿了(b)分,第二场比赛前(100)名都拿(c)分,在第一场比赛都拿(d)分能保证第(100)名分数最小。

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

    C

    题意:给你(q,p),求最大的整数(x),满足(x)整数(1)但没有被(p)整除。

    做法:把(p)质因数分解为:(a_{1}^{b_{1}}a_2^{b_2}...a_{k}^{b_{k}}),然后设(x=q),只要(x)中含任意一个(a_{i})因子个数的数量小于(b_{i})即可,用一个变量记录让哪个因子小于(b_{i})的代价最小,最后除一下就行了。

    时间复杂度:(O(很小))

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using  namespace  std;
    typedef  long  long  LL;
    inline  LL  ksm(LL  x,LL  y)
    {
    	LL  ans=1;
    	for(LL  i=1;i<=y;i++)ans*=x;
    	return  ans;
    }
    inline  LL  mymin(LL  x,LL  y){return  x<y?x:y;}
    int  main()
    {
    //	freopen("std.in","r",stdin);
    //	freopen("vio.out","w",stdout);
    	int  T;
    	scanf("%d",&T);
    	while(T--)
    	{
    		LL  x,y;
    		scanf("%lld%lld",&x,&y);
    		LL  ed=sqrt(y)+1;
    		
    		LL  ans=x;
    		LL  shit=(LL)999999999999999999;//赛后才发现这个地方少打了几个9
    		for(LL  i=2;i<=ed;i++)
    		{
    			if(y%i==0)
    			{
    				LL  cnt=0;
    				while(y%i==0)y/=i,cnt++;
    				LL  pre=0;
    				while(x%i==0)x/=i,pre++;
    				if(pre<cnt)
    				{
    					shit=1;
    					break;
    				}
    				else  shit=mymin(ksm(i,pre-cnt+1),shit);
    			}
    		}
    		if(y>1  &&  shit>1)
    		{
    			LL  pre=0;
    			while(x%y==0)x/=y,pre++;
    			if(pre<1)shit=1;
    			else  shit=mymin(ksm(y,pre),shit);
    		}
    		printf("%lld
    ",ans/shit);
    	}
    	return  0;
    }
    
    

    D

    题意: 给你长度为(2n)的数组,将其分成两个数组(q,p),然后(q)从大到小排序,(p)从小到大排序,这次拆分的价值为:(sumlimits_{i=1}^{n}|q_{i}-p_{i}|)

    然后问你所有拆分的价值,模(998244353)

    做法:额,首先,如果所有数字不同,不难发现,不管你怎么分,最大的那(n)个数字刚好分在(q,p)大的那一端,无法互相减去,所以不管你怎么分,价值都等于最大的(n)个数字减最小的(n)个数字,那万一数字相同呢?我们认为同样的数字,在原数组中所处下标越小,其就越小,且在排序的时候比较大小也这么认为,那么一样可以得到同样的结论,然后只需要乘上拆分的个数即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define  N  310000
    using  namespace  std;
    typedef  long  long  LL;
    LL  mod=998244353;
    inline  LL  ksm(LL  x,LL  y)
    {
    	LL  ans=1;
    	while(y)
    	{
    		if(y&1)ans=(ans*x)%mod;
    		x=(x*x)%mod;y>>=1;
    	}
    	return  ans;
    }
    LL  a[N],fc[N],sum;
    int  n; 
    int  main()
    {
    	scanf("%d",&n);
    	int  ed=n*2;
    	for(int  i=1;i<=ed;i++)scanf("%d",&a[i]);
    	fc[0]=1;for(int  i=1;i<=ed;i++)fc[i]=(LL)(fc[i-1]*i)%mod;
    	sort(a+1,a+ed+1);
    	for(int  i=1;i<=n;i++)sum+=a[n+i]-a[i];
    	sum%=mod;
    	LL  shit=ksm(fc[n],mod-2);
    	printf("%lld
    ",sum*shit%mod*shit%mod*fc[ed]%mod);
    	return  0;
    }
    

    E

    题意: 给你(n)个点,(m)条边,然后每个点都属于一个学术团队,有(k)个学术团队,求满足要求的二元组((i,j)),要求为:(i<j),且第(i)个团队和第(j)个团队形成的诱导子图为二分图。

    做法:
    做法1: 先默认所有二元组都可以,找不可以的,只要用并查集先处理出每个团队自己的,然后再针对每个团队和其他团队的即可。

    然后再两个团队判断完之后记得还原并查集,当然,需要注意的时,并查集不能路径压缩,不然还原并查集的时间复杂度就不是(O(m))的了,只能按秩合并。

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

    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    #define  N  510000
    using  namespace  std;
    typedef  long  long  LL;
    int  fa[N],val[N],siz[N],zanval;
    int  findfa(int  x)
    {
    	zanval=0;
    	int  y=x;
    	while(fa[y]!=y)zanval^=val[y],y=fa[y];
    	return  y;
    }
    inline  bool  mer(int  x,int  y,int  type)//合并
    {
    	int  tx=findfa(x);type^=zanval;
    	int  ty=findfa(y);type^=zanval;
    	if(tx==ty)
    	{
    		if(type==1)return  0;
    	}
    	else
    	{
    		if(siz[tx]>siz[ty])fa[ty]=tx,siz[tx]+=siz[ty],val[ty]=type;
    		else  fa[tx]=ty,siz[ty]+=siz[tx],val[tx]=type;
    	}
    	return  1;
    }
    struct  node
    {
    	int  id;
    	int  x,y;
    	node(int  idx=0,int  xx=0,int  yx=0){id=idx;x=xx;y=yx;}
    };
    vector<node> fuck[N];//每个团队向其余团队的边
    node  sta[N];int  top;
    int  n,m,k,be[N];
    bool  shit[N]/*判断一个联通块本身自己是不是二分图*/;int  shitcnt;//合法的团队
    bool  tis[N];
    int  pre[N],pre_top;
    inline  void  check(int  x)//判断这个点的祖先完事之后是否需要还原
    {
    	x=findfa(x);
    	if(!tis[x])
    	{
    		tis[x]=1;
    		pre[++pre_top]=x;
    	}
    }
    inline  bool  solve(int  l,int  r)//判断两个团队是否是二分图
    {
    	pre_top=0;
    	for(int  i=l;i<=r;i++)
    	{
    		int  x=sta[i].x,y=sta[i].y;
    		check(x);check(y);
    		if(!mer(x,y,1))return  1;
    	}
    	return  0;
    }
    inline  void  put_hui()//恢复并查集
    {
    	for(int  i=1;i<=pre_top;i++)fa[pre[i]]=pre[i],val[pre[i]]=0,tis[pre[i]]=0;
    }
    inline  bool  cmp(node  x,node  y){return  x.id<y.id;}
    int  main()
    {
    	scanf("%d%d%d",&n,&m,&k);
    	shitcnt=k;
    	for(int  i=1;i<=n;i++){scanf("%d",&be[i]);fa[i]=i;siz[i]=1;}
    	for(int  i=1;i<=m;i++)
    	{
    		int  x,y;scanf("%d%d",&x,&y);
    		if(be[x]==be[y])
    		{
    			if(!shit[be[x]])
    			{
    				if(!mer(x,y,1))
    				{
    					shit[be[x]]=1;
    					shitcnt--;
    				}
    			}
    		}
    		else
    		{
    			fuck[be[x]].push_back(node(be[y],x,y));
    			fuck[be[y]].push_back(node(be[x],y,x));
    		}
    	}
    	LL  ans=(LL)shitcnt*(shitcnt-1)/2;
    	LL  fei=0;
    	for(int  i=1;i<=k;i++)
    	{
    		if(shit[i])continue;
    		top=fuck[i].size();
    		for(int  j=0;j<top;j++)sta[j+1]=fuck[i][j];
    		sort(sta+1,sta+top+1,cmp);//使得id非严格单调递增
    		int  tmp=sta[1].id,tmppre=1;
    		for(int  j=1;j<=top;j++)
    		{
    			if(sta[j].id!=tmp)
    			{
    				if(!shit[tmp])
    				{
    					fei+=solve(tmppre,j-1);
    					put_hui();
    				}
    				tmppre=j;
    				tmp=sta[j].id;
    			}
    		}
    		if(top  &&  !shit[sta[top].id])
    		{
    			fei+=solve(tmppre,top);
    			put_hui();
    		}
    	}
    	printf("%lld
    ",ans-fei/2/*别忘了除2*/);
    	return  0;
    }
    

    做法2:先Orz 一波ZWQ,其会(O(n+m))的做法,首先,对每个团队跑一遍二分图染色,处理出这个团队必须对立的两个部分,例如(1,3)(2,4)必须对立,那么(1,3)缩成一个点(a)(2,4)缩成一个点(b)(a,b)连边(当然,为了保证时间复杂度是正确的,建议用(match)数组记录这条边,防止用边目录),需要注意的是,团队里可能不止一个必须对立的两个部分。然后如果(1,3)缩成了(a)点,那么其连边都变成(a)点连的边,最后只需要判断两个集合之间是否存在奇数环即可(二分图染色),当然,稍微注意一些细节:两个团队之间的边用(vector)存,只对两个团队之间边的端点跑二分图等等。

    这样,就能保证时间复杂度:(O(n+m))

    当然,我是听(ZWQ)讲完口胡的。。。

  • 相关阅读:
    js定时相关函数:
    远程控制使用kill软件映射内网进行远程控制(9.28 第十四天)
    PHP基础(9.27 第十三天)
    使用kali中的Metasploit通过windows7的永恒之蓝漏洞攻击并控制win7系统(9.27 第十三天)
    Nmap目录扫描和漏洞扫描(9.27 第十三天)
    JS正则和点击劫持代码(第十二天 9.27)
    Banner信息收集和美杜莎使用(9.26 第十二天)
    JavaScript的运算符、条件判断、循环、类型转换(9.25 第十一天)
    使用BurpSuite和Hydra爆破相关的服务(9.25 第十一天)
    JavaScript(第十一天 9.24)
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/13915329.html
Copyright © 2020-2023  润新知