zoukankan      html  css  js  c++  java
  • CF Round #677 div3 赛后总结

    前言

    运气真好AK了(╯‵□′)╯︵┻━┻

    庆祝(E)题没看懂题找规律过了(╯‵□′)╯︵┻━┻

    A

    题意:
    差不多就是说,在(1-10000)范围内,如果一个数字的每一位都是同一个数字,就叫无聊数字,然后会以(1,11,111,1111,2,22...)这样的顺序排列,然后问无聊数字(x),以及其的排列前面的数字的位数之和是多少,例如:(11),前面有(1),然后位数之和为(2+1=3)

    暴力模拟啊。

    #include<cstdio>
    #include<cstring>
    using  namespace  std;
    inline  int  solve(int  x)
    {
    	int  type=x%10;
    	int  ans=(type-1)*10;
    	int  y=0;
    	while(x)
    	{
    		x/=10;
    		y++;
    		ans+=y;
    	}
    	printf("%d
    ",ans);
    }
    int  main()
    {
    	int  T;scanf("%d",&T);
    	for(int  i=1;i<=T;i++)
    	{
    		int  x;scanf("%d",&x);
    		solve(x);
    	}
    	return  0;
    }
    

    B

    题意:
    如果第(i)个位置有书为(1),没书为(0)
    对于书架上连续的一段书([l,r]),如果(r+1)的位置没有位置,则可以把([l,r])的书右移到([l+1,r+1]),左移类似。
    然后问把所有书变成连续的一段最小需要多少移动次数。

    做法:
    每次最多消掉一个间隔,不难发现,答案就是相邻每段书之间的间隔和。

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

    C

    题意:
    差不多就是说,一条鱼(i),如果(a[i-1]<a[i])或者(a[i+1]<a[i]),就可以吃掉(i-1)或者(i+1)的位置,然后(a[i]++),如果一条鱼,假定全局只有它能吃别的鱼,并且在最后它能吃掉所有的鱼,那么称其为优势鱼,然后问存不存在优势鱼,存在,随便输其中一条优势鱼的编号。

    首先考虑权值最大的情况,那如果权值最大的鱼吃掉了一条鱼,那么所有的鱼它随便吃,因此只要存在一只权值最大的鱼且左右两边有一条鱼比它小,它就是优势鱼。

    当时这样一定能判定没有优势鱼的情况吗?不难发现,这种做法找到的优势鱼一定是正确的,且如果找不到当且仅当所有的鱼权值相同,此时确实是没有优势鱼的,所以这种做法是正确的。

    #include<cstdio>
    #include<cstring>
    #define  N  310000
    using  namespace  std;
    int  a[N],n;
    inline  int  mymax(int  x,int  y){return  x>y?x:y;}
    void  solve()
    {
    	scanf("%d",&n);
    	int  mmax=1;
    	for(int  i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		mmax=mymax(a[i],mmax);
    	}
    	a[0]=a[1];a[n+1]=a[n];
    	for(int  i=1;i<=n;i++)
    	{
    		if(a[i]==mmax  &&  (a[i-1]!=a[i]  ||  a[i+1]!=a[i]))
    		{
    			printf("%d
    ",i);
    			return  ;
    		}
    	}
    	printf("-1
    ");
    	return  ;
    }
    int  main()
    {
    	int  T;scanf("%d",&T);
    	while(T--)solve();
    	return  0;
    }
    

    同机房奆佬还想了一个类似栈的做法,不再赘述。虽然刚开始思路错了,但是后来发现改一下就能规避问题了。

    D

    题意:
    每个点都有一个权值,然后让你用(n-1)条边把(n)个点连成一棵树,且要求边两端的点权值不能相同。

    记录第一个点的颜色,找到第一个和第一个点不同颜色的点,记为(id),如果一个点和第一个点不同颜色,则连向第一个点,如果和第一个点相同,则连向(id)

    无解情况就是找不到(id)的情况。

    #include<cstdio>
    #include<cstring>
    #define  N  5100
    using  namespace  std;
    int  co[N],n;
    void  solve()
    {
    	scanf("%d",&n);
    	for(int  i=1;i<=n;i++)
    	{
    		scanf("%d",&co[i]);
    	}
    	int  shit=co[1];
    	int  id=0;
    	for(int  i=2;i<=n;i++)
    	{
    		if(co[i]!=shit)
    		{
    			id=i;
    			break;
    		}
    	}
    	if(!id)
    	{
    		printf("NO
    ");
    		return  ;
    	}
    	printf("YES
    ");
    	for(int  i=2;i<=n;i++)
    	{
    		if(co[i]!=shit)printf("1 %d
    ",i);
    		else  printf("%d %d
    ",id,i);
    	}
    }
    int  main()
    {
    	int  T;scanf("%d",&T);
    	while(T--)solve();
    	return  0;
     } 
    

    E

    题意:
    赛后才知道题意
    首先,轮舞是什么?
    篝火晚宴见过吧,手拉着手一个圈跳舞就是轮舞,现在把(n(n是偶数))个人(第(i)个人编号为(i))等分成两个轮舞。
    记为一种轮舞方案{(A,B)}。
    两个轮舞(X,Y)相同,当且仅当选定某个点为开头时,从左往右形成的序列相同(也就是说([1,2,3])([3,2,1])是不同的)。
    轮舞方案{(A_{1},B_1)}和{(A_{2},B_2)}相同,当且仅当(A_1=A_2,B_1=B_2)或者(A_1=B_2,B_1=A_2)
    问有多少个不同的方案,答案保证在(long) (long)范围内。

    做法:
    (n=2m),先选(m)个人组成左边的圈,即(C_{n}^m),但是选出了(m)个人,不同的圈排列个数是多少呢?我们不妨像题目中所说的,固定一个人就是在一号位置,其余人全排列,那么就是((n-1)!),不难发现这样不会重复且可以找到所有的方案(显然正确因为每个人一定在圈上),然后就是(C_{n}^{m}*(m-1)!*(m-1)!),但是发现一个圈方案左边右边都会被选上,所以还要除(2)(其实有规避的方法,假设编号为(1)的人一定被选上,那么就是(C_{n-1}^{m-1}))。

    化一下式子:
    (frac{C_{n}^{m}*(m-1)!*(m-1)!}{2}=frac{n!*(m-1)!*(m-1)!}{2*m!*m!}=frac{2n!}{n^2}=frac{2(n-1)!}{n})

    这个式子就简单了很多。这个就是我考场上发现的规律。

    打开计算器,发现(19!)不会爆(long) (long),直接乱搞。

    #include<cstdio>
    #include<cstring>
    using  namespace  std;
    int  main()
    {
    	int  n;scanf("%d",&n);
    	if(n==2)printf("1
    ");
    	else
    	{
    		long  long  ans=1;
    		for(int  i=1;i<n;i++)ans*=i;
    		ans/=n/2;
    		printf("%lld
    ",ans);
    	}
    	return  0;
    }
    

    F

    一个(n*m)的矩阵,每一行最多能选(frac{m}{2})个,然后要求选出来数的总和是(k)的倍数,问总和的最大值是多少。

    (f[i][j][k])表示每一行的第(i)个数字,选了(j)个,余数为(k)的最大值,不难想到状态转移方程。

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

    #include<cstdio>
    #include<cstring>
    #define  N  90
    using  namespace  std;
    inline  int  mymax(int  x,int  y){return  x>y?x:y;}
    int  a[N][N];
    int  f[2][N][N];
    int  n,m,K;
    void  dp()
    {
    	memset(f[0],-20,sizeof(f[0]));
    	f[0][0][0]=0;int  now=0,pre=1;
    	int  limit=m/2;
    	for(int  i=1;i<=n;i++)
    	{
    		now^=1;pre^=1;
    		memset(f[now],-20,sizeof(f[now]));
    		for(int  j=0;j<=limit;j++)
    		{
    			for(int  k=0;k<K;k++)f[now][0][k]=mymax(f[now][0][k],f[pre][j][k]);
    		}
    		for(int  j=1;j<=m;j++)
    		{
    			now^=1;pre^=1;
    			memset(f[now],-20,sizeof(f[now]));
    			for(int  k=1;k<=limit;k++)
    			{
    				for(int  t=0;t<K;t++)
    				{
    					int  shit=(t+a[i][j])%K;
    					f[now][k][shit]=mymax(f[now][k][shit],f[pre][k-1][t]+a[i][j]);
    				}
    			}
    			for(int  k=0;k<=limit;k++)
    			{
    				for(int  t=0;t<K;t++)f[now][k][t]=mymax(f[now][k][t],f[pre][k][t]);
    			}
    		}
    	}
    	now^=1;pre^=1;
    	memset(f[now],-20,sizeof(f[now]));
    	for(int  j=0;j<=limit;j++)
    	{
    		for(int  k=0;k<K;k++)f[now][0][k]=mymax(f[now][0][k],f[pre][j][k]);
    	}
    	printf("%d
    ",f[now][0][0]);
    }
    int  main()
    {
    	scanf("%d%d%d",&n,&m,&K);
    	for(int  i=1;i<=n;i++)
    	{
    		for(int  j=1;j<=m;j++)scanf("%d",&a[i][j]);
    	}
    	dp();
    	return  0;
    }
    

    G

    题意:
    一个(n)个点(m)条边的无向图,有(k)个订单,每个订单是从(x)跑到(y),定义每个订单的费用为(x)(y)的最短路,总费用为每个订单的费用和。

    你最多可以让一条边变成(0),然后问你最小总费用是多少。

    做法:
    对每个点跑一遍最短路,因为是稀疏图,(SPFA)很快。为了不被hack,后面又打了一个Dij的版本

    肯定用掉机会比不用最小费用更小一点(最多不变)。

    枚举是哪条边变成了(0),对于订单(x->y),有两种操作,一种走原来的路,一种为了走(0)边而强行改变路线(可能没改),至于怎么让(x->y)强行走一条边,大家也是懂的啦,直接把(x)走到边的一端的费用再加上另一端走到(y)的费用即可。

    Dij时间复杂度:(O(nmlog{m}+mk))
    SPFA:

    #include<cstdio>
    #include<cstring>
    #define  N  1100
    #define  M  2100
    using  namespace  std;
    typedef  long  long  LL;
    inline  LL  mymin(LL  x,LL  y){return  x<y?x:y;}
    LL  d[N][N];
    int  list[N],head,tail,n,m,k;
    bool  v[N];
    struct  node
    {
    	int  y,next;
    	LL  c;
    }a[M];int  len,last[N];
    inline  void  ins(int  x,int  y,LL  c){len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;} 
    void  SPFA(int  st)
    {
    	memset(d[st],20,sizeof(d[st]));d[st][st]=0;
    	list[head=1]=st;tail=2;v[st]=1;
    	while(head!=tail)
    	{
    		int  x=list[head++];if(head==n+1)head=1;
    		v[x]=0;
    		for(int  k=last[x];k;k=a[k].next)
    		{
    			int  y=a[k].y;
    			if(d[st][x]+a[k].c<d[st][y])
    			{
    				d[st][y]=d[st][x]+a[k].c;
    				if(!v[y])
    				{
    					v[y]=1;
    					list[tail++]=y;if(tail==n+1)tail=1;
    				}
    			}
    		}
    	}
    }
    struct  SHIT
    {
    	int  x,y;
    	SHIT(int  xx=0,int  yy=0){x=xx;y=yy;}
    }zjj1[N],zjj2[N];
    int  main()
    {
    	scanf("%d%d%d",&n,&m,&k);
    	for(int  i=1;i<=m;i++)
    	{
    		int  x,y;LL  c;scanf("%d%d%lld",&x,&y,&c);
    		ins(x,y,c);ins(y,x,c);
    		zjj2[i]=SHIT(x,y);
    	}
    	for(int  i=1;i<=k;i++)
    	{
    		int  x,y;
    		scanf("%d%d",&x,&y);
    		zjj1[i]=SHIT(x,y);
    	}
    	for(int  i=1;i<=n;i++)SPFA(i);
    	LL  ans=(LL)99999999999999;
    	for(int  i=1;i<=m;i++)
    	{
    		LL  sum=0;
    		int  x=zjj2[i].x,y=zjj2[i].y;
    		for(int  j=1;j<=k;j++)
    		{
    			int  ax=zjj1[j].x,ay=zjj1[j].y;
    			sum+=mymin(mymin(d[ax][x]+d[y][ay],d[ax][y]+d[x][ay]),d[ax][ay]);
    		}
    		ans=mymin(ans,sum);
    	}
    	printf("%lld
    ",ans);
    	return  0;
    }
    

    Dij:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define  N  1100
    #define  M  2100
    using  namespace  std;
    typedef  long  long  LL;
    inline  LL  mymin(LL  x,LL  y){return  x<y?x:y;}
    LL  d[N][N];
    int  n,m,k;
    bool  v[N];
    struct  node
    {
    	int  y,next;
    	LL  c;
    }a[M];int  len,last[N];
    inline  void  ins(int  x,int  y,LL  c){len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}
    priority_queue<pair<LL,int>,vector<pair<LL,int> >,greater<pair<LL,int> > > fuck;
    void  SPFA(int  st)
    {
    	memset(d[st],20,sizeof(d[st]));d[st][st]=0;
    	memset(v,0,sizeof(v));
    	while(!fuck.empty())fuck.pop();
    	fuck.push(make_pair(0,st));
    	for(int  i=1;i<=n;i++)
    	{
    		pair<LL,int>  zwq=fuck.top();fuck.pop();
    		int  x=zwq.second;
    		while(v[x])
    		{
    			zwq=fuck.top();fuck.pop();
    			x=zwq.second;
    		}
    		v[x]=1;
    		for(int  k=last[x];k;k=a[k].next)
    		{
    			int  y=a[k].y;
    			if(!v[y]  &&  zwq.first+a[k].c<d[st][y])
    			{
    				d[st][y]=zwq.first+a[k].c;
    				fuck.push(make_pair(d[st][y],y));
    			}
    		}
    	}
    }
    struct  SHIT
    {
    	int  x,y;
    	SHIT(int  xx=0,int  yy=0){x=xx;y=yy;}
    }zjj1[N],zjj2[N];
    int  main()
    {
    	scanf("%d%d%d",&n,&m,&k);
    	for(int  i=1;i<=m;i++)
    	{
    		int  x,y;LL  c;scanf("%d%d%lld",&x,&y,&c);
    		ins(x,y,c);ins(y,x,c);
    		zjj2[i]=SHIT(x,y);
    	}
    	for(int  i=1;i<=k;i++)
    	{
    		int  x,y;
    		scanf("%d%d",&x,&y);
    		zjj1[i]=SHIT(x,y);
    	}
    	for(int  i=1;i<=n;i++)SPFA(i);
    	LL  ans=(LL)99999999999999;
    	for(int  i=1;i<=m;i++)
    	{
    		LL  sum=0;
    		int  x=zjj2[i].x,y=zjj2[i].y;
    		for(int  j=1;j<=k;j++)
    		{
    			int  ax=zjj1[j].x,ay=zjj1[j].y;
    			sum+=mymin(mymin(d[ax][x]+d[y][ay],d[ax][y]+d[x][ay]),d[ax][ay]);
    		}
    		ans=mymin(ans,sum);
    	}
    	printf("%lld
    ",ans);
    	return  0;
    }
    
  • 相关阅读:
    【BZOJ1489】[HNOI2009]双递增序列(动态规划)
    【BZOJ1488】[HNOI2009]图的同构(Burside引理,Polya定理)
    【BZOJ4888】[TJOI2017]异或和(树状数组)
    【BZOJ1487】[HNOI2009]无归岛(动态规划)
    【BZOJ1485】[HNOI2009]有趣的数列(组合数学)
    【BZOJ1484】[HNOI2009]通往城堡之路 (贪心)
    【BZOJ1452】[JSOI2009]Count(树状数组)
    【BZOJ1449】[JSOI2009]球队收益(网络流,费用流)
    【BZOJ1444】[JSOI2009]有趣的游戏(高斯消元,AC自动机)
    【BZOJ1434】[ZJOI2009]染色游戏(博弈论)
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/13851058.html
Copyright © 2011-2022 走看看