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\n",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\n",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\n",i);
			return  ;
		}
	}
	printf("-1\n");
	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\n");
		return  ;
	}
	printf("YES\n");
	for(int  i=2;i<=n;i++)
	{
		if(co[i]!=shit)printf("1 %d\n",i);
		else  printf("%d %d\n",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\n");
	else
	{
		long  long  ans=1;
		for(int  i=1;i<n;i++)ans*=i;
		ans/=n/2;
		printf("%lld\n",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\n",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(nm\log{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\n",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\n",ans);
	return  0;
}
posted @ 2020-10-21 10:46  敌敌畏58  阅读(115)  评论(0编辑  收藏  举报