Codeforces Round 916 (Div. 3)补题

Problemsolving Log(Problem - A - Codeforces)

题目大意:玩家可以在1分钟内解决A,2分钟内解决B,...。现有一个日志,可以知道玩家每分钟在思考哪个题,据此判断玩家总共解出几道题。

思路:实际上只用统计玩家对于每道题花费了多长时间,是否符合规定的解题时间即可。

#include
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		mapmp;
		string s;
		cin>>n>>s;
		for(int i=0;i=(c-'A'+1)) 
			{
				flag++;
			}
		}
		printf("%d\n",flag); 
	}
}

Preparing for the Contest(Problem - B - Codeforces) 

题目大意:用数字表示题目的难度系数,数字大的题目更难,玩家每解决一个比前一个题目难的题目就会兴奋一次,现有n个题目,玩家总共兴奋了k次,给出一个合适的序列,表示解决题目的顺序。

思路:我们先将数字倒序排列,然后根据兴奋次数,将前k+1个数字逆转即可。

#include
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,k;
		scanf("%d%d",&n,&k);
		vectora(n);
		for(int i=0;i

Quests(Problem - C - Codeforces) 

题目大意:现有n个题目,已知对于第i个题目第一次做出来的收获是a[i],第二次及以后每次做出来的收获是b[i],想要做第i个题目,必须保证前i-1个题目全部都解决掉,现在可以做k个题目,问最大收获是多少。

思路:我们从头开始假设每次遍历到的题目做完就不做了,然后剩下的次数都用来做当前可以做的题目范围内b[i]最大的题目,这样遍历一遍,找出最大值。

#include
using namespace std;
int a[200010],b[200010];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,k;
		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]);
		int mx=0,t=0;
		long long sum=0,ans=0;
		for(int i=1;i<=n;i++)
		{
			mx=max(mx,b[i]);
			sum+=a[i];
			t++;
			ans = max(ans,sum+(k-t)*mx);
			if(t==k) break;
		}
		printf("%lld\n",ans);
	}
}

 Three Activities(Problem - D - Codeforces)

题目大意:现有三个活动,我们可知第i天做第1个活动有a[i]个人陪同,做第2个活动有b[i]个人陪同,做第3个活动有c[i]个人陪同,现要求每天只能做一项活动,要求选3天x,y,z,最后使ax+by+cz最大,输出最大值。

思路:这道题有两个量,同一时间只能进行一个活动,一个点是每个活动只进行一次。我们的解决办法是dfs来搜索。实际上再抽象一点就是讨论出决定每个活动的顺序,因为活动与活动之间的天数选择会相互影响,那么我们就指定选择顺序,然后去找在这个顺序下能得到的最大值。另外要注意,dfs的状态一定要清干净。

#include
using namespace std;
int vis[200010],st[10];
int ans,flag,n,mx;	
vector>p[4];
void dfs(int k)
{
	st[k]=1;
	int v,d,i;
	for(i=0;i>r,p[1].push_back({r,i}),vis[i]=0;
		for(int i=1;i<=n;i++) cin>>r,p[2].push_back({r,i});
		for(int i=1;i<=n;i++) cin>>r,p[3].push_back({r,i});
		sort(p[1].begin(),p[1].end());
		reverse(p[1].begin(),p[1].end());
		sort(p[2].begin(),p[2].end());
		reverse(p[2].begin(),p[2].end());
		sort(p[3].begin(),p[3].end());
		reverse(p[3].begin(),p[3].end());
		for(int i=1;i<=3;i++)
		{
			for(int i=1;i<=n;i++) vis[i]=0;
			st[1]=st[2]=st[3]=0;
			ans=0,flag=0;
			dfs(i);
		}
		printf("%d\n",mx);
		p[1].clear();
		p[2].clear();
		p[3].clear();
	}
}

Game with Marbles (Easy Version)(Problem - E1 - Codeforces) 

题目大意:现在有n种求,对于A和B,她们手中每个球分别有a[i]和b[i]个,现在进行一个游戏,A先开始,选择一种球,将手中的这种球扔掉一个,同时B需要扔掉手中所有的此类球,最后求出所有种类球a[i]-b[i]的和。A想使结果更大,B想使结果更小,问两人都采取最佳策略,最后的结果是多少。(easy veesion:n<=6)

思路:博弈论问题,现在就是要找最优策略,我们先求出原来每种球的y=a[i]-b[i]的值,然后求出A对每种球操作后得到的值ga[i]以及前后的差值ca[i]=ga[i]-y,以及b对每种球操作后的值gb[i]以及前后差值cb[i]=gb[i]-y,A肯定要选择ca[i]大同时cb[i]小的(cb[i]一般为负,操作后gb[i]小于等于0)的进行操作,B肯定选择cb[i]小,ca[i]大的进行操作,难免会出现相等的情况,那么对于这两种情况还要讨论两种不同的排序方式,太过麻烦,我们不如直接以ca[i]-cb[i]作为标准进行排序,直接就可以得出结果。

#include
using namespace std;
#define int long long
int a[200010],b[200010];
struct zb
{
	int ca,cb,ga,gb,y;
}c[200010];
bool cmp(zb x,zb y)
{
	return x.y>y.y;
}
signed main()
{
	int t;
	scanf("%lld",&t);
	while(t--)
	{
		int n;
		scanf("%lld",&n);
		for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
		for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
		for(int i=1;i<=n;i++)
		{
			int y=a[i]-b[i];
			int ca=(a[i]-1)-y;
			int cb=(1-b[i])-y;
			int ga=a[i]-1,gb=1-b[i];
			int dis=ca-cb;
			c[i]={ca,cb,ga,gb,dis};
		}
		sort(c+1,c+1+n,cmp);
		int sum=0;
		for(int i=1;i<=n;i++)
		{
			int d;
			if(i%2) 
			{
				sum += c[i].ga;
			}
			else 
			{
				sum += c[i].gb;
			}
		}
		printf("%lld\n",sum);
	}
}

 Game with Marbles (Hard Version)(Problem - E2 - Codeforces) 

题目大意:跟上一个其实差不多,只是数据范围扩大了而已。

思路:我们上面讨论的策略实际上已经很完备了,那么就没什么说的了,直接把数据范围扩大即可。这种题目最重要的就是找出最优策略,每个人都会采取当前局势下,对自己利益最大化的方式进行操作,我们要找出策略,并尽量简化讨论,将两者操作的共同点找出来。

#include
using namespace std;
#define int long long
int a[200010],b[200010];
struct zb
{
	int ca,cb,ga,gb,y;
}c[200010];
bool cmp(zb x,zb y)
{
	return x.y>y.y;
}
signed main()
{
	int t;
	scanf("%lld",&t);
	while(t--)
	{
		int n;
		scanf("%lld",&n);
		for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
		for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
		for(int i=1;i<=n;i++)
		{
			int y=a[i]-b[i];
			int ca=(a[i]-1)-y;
			int cb=(1-b[i])-y;
			int ga=a[i]-1,gb=1-b[i];
			int dis=ca-cb;
			c[i]={ca,cb,ga,gb,dis};
		}
		sort(c+1,c+1+n,cmp);
		int sum=0;
		for(int i=1;i<=n;i++)
		{
			int d;
			if(i%2) 
			{
				sum += c[i].ga;
			}
			else 
			{
				sum += c[i].gb;
			}
		}
		printf("%lld\n",sum);
	}
}

Programming Competition(Problem - F - Codeforces)

题目大意:有一个公司,1为公司负责人,每个员工有个直属的上级,同时她直属上级的上级也是她的上级,现在要对员工进行分组,两人一组,每个员工不能和她的上级分在一组里面,问最多能分多少组。

思路:我们可以发现,抛开第一个点,可以得到若干个连通块,虽然连通块内部每个点可以和它的非领导节点相连,但是显然连通块之间相连更加无可争议,也更好处理,因为不用考虑点与点之间的关系。这时候我们就遇到了第一个点,一个点的子树数量的统计,这个实际上用深搜的思路就可以实现。然后我们就要考虑,联通块之间相互匹配,会不会有剩下的所有点都在一个连通块内部,这种情况也不能随便处理。那么怎么样会出现这种情况呢?显然就是最多的那棵子树的数量多余其他所有的点的和,那么其他所有的点都耗尽了,这棵树中还是有点会被剩下,那么对于被剩下的点,我们怎么处理呢?实际上我们可以记录它其中有多少点已经匹配了,再去找它的子连通块进行匹配,如此递归下去,就能解决。另外为了尽可能地减少递归,我们假设已经配对地点全在最大子树中,这样就可以减少最大子树的点数,因为只有最大的子树的点数可能超过总数的一半,所以不用担心最大子树的点数减掉已经配对的点后,变成不是最大的,反正剩下的点数也少于总点数的一半,并没有什么影响。而且我们讨论一下最大点数不减去k与其他点数和进行比较的情况会发现不会对比较产生什么影响。原最大的小于其他的和,那么大家相互之间配一配,k均摊开即可(减k后仍然是小于的);若等于,也是可以相互配,把k均摊开(减k后小于);若大于,减后要么可以均摊要么就仍大于还是得进行递归,所以没什么影响。

#include
using namespace std;
int h[200010],ne[200010],e[200010],z[200010],vis[200010],idx;
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void zs(int k)
{
	z[k]=1;
	for(int i=h[k];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(!vis[j])//这里其实可以不加判断,因为有向图是不会倒回去的
		{	
			zs(j);
			z[k] += z[j];
		}
	}
}
int tj(int v,int k)//k是以v为根的子树中已经配对的点的数量
{
	int all=0,mx=-1;
	for(int i=h[v];i!=-1;i=ne[i])
	{
		int j=e[i];
		all += z[j];
		if(mx==-1||z[mx]

你可能感兴趣的:(codeforces补题,算法)