Codeforces Round #788 (Div. 2)

A. Prof. Slim

题目链接:Problem - A - Codeforces

样例输入: 

4
7
7 3 2 -11 -13 -17 -23
6
4 10 25 47 71 96
6
71 -35 7 -4 -11 -25
6
-45 9 -48 -67 -55 7

样例输出:

NO
YES
YES
NO

题意:给你一个长度为n的数组,我们可以对这个数组进行无限次操作,每次操作可以选择两个数然后交换两个数的符号,问能否通过操作使得操作后的序列是一个非递减序列。

分析:因为答案是一个非递减序列,那么所有的负数都应该尽可能集中在数组的前面,而且如果一个数是正数,那么他的后面绝对不能够出现负数,所以我们应该统计负数的数目,然后把负数都安排在前面,然后检查一下是否是非递减的即可。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=2e5+10;
int a[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		int cnt=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if(a[i]<0) cnt++;
		}
		bool flag=true;
		for(int i=2;i<=cnt;i++)
			if(abs(a[i])<=abs(a[i-1])) continue;
			else flag=false;
		for(int i=cnt;i<n;i++)
			if(abs(a[i])<=abs(a[i+1])) continue;
			else flag=false;
		if(flag) puts("YES");
		else puts("NO");
	}
	return 0;
} 

B. Dorms War

题目链接:Problem - B - Codeforces

样例输入: 

10
9
iloveslim
1 s
7
joobeel
2 o e
7
basiozi
2 s i
6
khater
1 r
7
abobeih
6 a b e h i o
5
zondl
5 a b c e f
6
shoman
2 a h
7
shetwey
2 h y
5
samez
1 m
6
mouraz
1 m

样例输出:

5
2
3
5
1
0
3
5
2
0

题意:给定一个长度为n的字符串,然后给定若干个特殊字符,每一轮过后位于特殊字符前面的字符都会被去除(即使该字符是特殊字符),直至字符串长度不再发生变化为止,问操作的轮数。

分析:我们考虑两个特殊字符,这两个特殊字符中间是没有其他特殊字符的,但这两个特殊字符之间有k个字符,前一个特殊字符到他前面一个特殊字符之间的距离是m(这里假设m>k),每轮过后两个特殊字符的距离就会减1,那么某一刻这两个特殊字符相邻后前一个字符到他前面一个特殊字符之间的距离就变为m-k,接下来一个特殊字符消失,这个时候后面的特殊字符到他左边的特殊字符的距离就变为m-k-1,跟原来特殊字符没消失的距离是一样的(原来特殊字符如果没有消失,那么他离他前面的一个特殊字符的距离就是m-k-1),由此我们可以得到只需要求出两个中间无特殊字符的特殊字符之间的最大距离即可。需要注意的一点是我们应该把第一个字符看成是特殊字符。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int N=2e5+10;
char s[N];
map<char,int>mp;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d%s",&n,s+1);
		int m;
		scanf("%d",&m);
		mp.clear();
		for(int i=1;i<=m;i++)
		{
			char op[2];
			scanf("%s",op);
			mp[op[0]]=1;
		}
		int lastid=1;
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			if(mp[s[i]])
			{
				ans=max(ans,i-lastid);
				lastid=i;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

C. Where is the Pizza?

题目链接:Problem - C - Codeforces

样例输入:

9
7
1 2 3 4 5 6 7
2 3 1 7 6 5 4
2 0 1 0 0 0 0
1
1
1
0
6
1 5 2 4 6 3
6 5 3 1 4 2
6 0 0 0 0 0
8
1 6 4 7 2 3 8 5
3 2 8 1 4 5 6 7
1 0 0 7 0 3 0 5
10
1 8 6 2 4 7 9 3 10 5
1 9 2 3 4 10 8 6 7 5
1 9 2 3 4 10 8 6 7 5
7
1 2 3 4 5 6 7
2 3 1 7 6 5 4
0 0 0 0 0 0 0
5
1 2 3 4 5
1 2 3 4 5
0 0 0 0 0
5
1 2 3 4 5
1 2 3 5 4
0 0 0 0 0
3
1 2 3
3 1 2
0 0 0

样例输出:

4
1
2
2
1
8
1
2
2

题意:每次给出两个1~n的全排列a[]和b[],问对于每一个i都有c[i]=a[i]或者c[i]=b[i],其中给定一个c[i],如果c[i]=0代表这个位置可以选择a[i]和b[i]中的一个,否则c[i]就是已经选好的一个数,问c[]是一个全排列的方案数。

分析:我们先不考虑有元素已经固定的情况,假设对于每个i都可以任选c[i]为a[i]和b[i]中的一个,那么这个时候的方案我们应该怎么考虑呢?

以该样例为例子进行说明:

5
1 2 3 4 5
1 2 3 5 4
0 0 0 0 0

我们发现前三个位置a[]=b[],也就是说前三个位置都已经固定,我们来看第四个位置,可以取4也可以取5,不妨取4,也就是取a[i],那么当b[j]=4时,对于c[j]我们将不能取b[j],而只能取a[j],同样的我们再找一个b[k]=a[j]我们也只能取a[k],直到某个b[l]=a[4],那么这个时候就构成一个环,也就是说环中的一个元素一旦确定,那么这整个环里面的元素都是已经确定的,所以对于一个长度不为2的环(包含a[]和b[],也就是说环中的元素不完全相同),我们一共有两种选择,那么我们就可以按照这个方法寻找不重复的且环的长度大于2的环的个数,每个环对答案的贡献都是一个因子2.

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=2e5+10,mod=1e9+7;
int a[N],b[N],c[N],mp[N];
bool vis[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]),mp[a[i]]=i,vis[i]=false;
		for(int i=1;i<=n;i++)
			scanf("%d",&b[i]);
		for(int i=1;i<=n;i++)
			scanf("%d",&c[i]);
		int ans=1;
		for(int i=1;i<=n;i++)
		{
			if(vis[a[i]]) continue;
			vis[a[i]]=true;
			bool flag=true;
			int j=i;
			if(c[j]) flag=false;
			while(true)
			{
				j=mp[b[j]];
				if(c[j]) flag=false;
				vis[a[j]]=true;
				if(b[j]==a[i]) break;
			}
			
			if(flag&&(i!=j)) ans=ans*2%mod;
		}
		printf("%d\n",ans);
	}
	return 0;
}

D. Very Suspicious

题目链接:Problem - D - Codeforces

 样例输入:

4
1
2
3
4567

样例输出:

2
2
3
83

题意:给定一个类似于上图的无限的图,多条直线可以形成若干个等边三角形,每次给定一个n,问至少多少条直线能够形成n个及以上的等边三角形。

分析:首先我们能够发现图中可画直线一共只有三种不同的斜率,不妨设斜率分别为k1,k2,k3,假设三种斜率的直线数目分别为t1,t2,t3,结合图像进行分析可以发现当我们增加一条直线时,等边三角形的数目就会增加与这条直线斜率不同的直线数目和的2倍,也就是说假如我们画一条斜率为k2的直线,那么答案就会在原来的基础上增加2*(t1+t3),按照这个想法我们就可以预处理出来若干条直线所能够形成的等边三角形的最大数目,然后对于每次询问直接对数组进行二分即可

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=1e7+10;
int f[N];
int main()
{
	f[2]=2;f[3]=6;
	int mx;
	for(mx=4;;mx++)
	{
		if(mx%3==0) f[mx]=f[mx-1]+mx/3*4;
		else if(mx%3==1) f[mx]=f[mx-1]+mx/3*4;
		else f[mx]=f[mx-1]+(mx/3*2+1)*2;
		if(f[mx]>1e9) break;
	}
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		int l=1,r=mx;
		while(l<r)
		{
			int mid=l+r>>1;
			if(f[mid]>=n) r=mid;
			else l=mid+1;
		}
		printf("%d\n",l);
	}
	return 0;
}

E. Hemose on the Tree

题目链接:Problem - E - Codeforces

样例输入:

2
2
1 2
2 3
3 4
3
1 2
2 3
3 4
1 5
1 6
5 7
5 8

样例输出:

3
5 1 3 6
4 2 7
5
1 2 8 11 4 13 9 15
6 14 3 7 10 5 12

题意:给定一个节点个数为2^p的 有根树,我们需要给每个节点和边一个权值,权值是位于[1,2^(p+1)-1],且所有边或者点的权值均不相同,然后任意选择一个根,使得根节点到任意边或者点的路径上的权值异或和的最大值最小。

分析:这显然是一个构造题,设节点个数为n,即n=2^p,先来看一下答案有没有可能小于n,其实这是不可能的,为什么这么说呢?因为如果根节点的权值大于等于n,那么他自身异或就是一个大于等于n的数了,如果根节点的权值小于n,那么我们选择一个点权大于n的点或者边权大于n的边,使得其从根节点到该点或者该边上没有其他大于n的权值,那么异或和的最高位一定是1,因为只有一个1,那么显然答案是不可能小于n的。

下面给出一种异或和为n的构造方法。我们发现权值是在[1,2*n-1]中的,对于任意的i<n都有i和i+n在二进制表示上只有最高位不同,我们就按照这个方法两两分成一组,那么每组内的数的异或值都是n,刚好空出来一个n作为根节点的权值,这也就保证了可以随意选择一个节点作为根节点,从根节点到某个节点必须要经过该节点前面的一条边,那么而如果我们把该节点和该节点前面的边的权值取同一个组内的两个值就相当于异或n,那么这个值肯定不会大于n(保证到上一个节点的路径异或和为0或者n),由于根节点的权值是n,那么由归纳法可知肯定是存在一条构造方法是满足题意的,对于到达前一个点的异或值等于n的边我们就取这个组内的较大值,这样异或值就小于n了,如果异或值等于0我们就取较小值,这样异或值也是小于n的,所以按照这种构造方法一定可以构造一个答案为n的解,上面又证明了答案不可能小于n,故n为最优解。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e6+10;
int h[N],e[N],ne[N],w1[N],w2[N],idx;
int n;
void add(int x,int y)
{
	e[idx]=y;
	ne[idx]=h[x];
	h[x]=idx++;
}
int cnt;
void dfs(int x,int fa,int val,int s)//s记录到达当前节点的异或值 
{
	w1[x]=val;
	cnt--;
	s^=w1[x];
	for(int i=h[x];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(j==fa) continue;
		if(s>=n)
		{
			w2[i/2]=cnt+n;
			dfs(j,x,cnt,s^w2[i/2]);
		}
		else
		{
			w2[i/2]=cnt;
			dfs(j,x,cnt+n,s^w2[i/2]);
		}
	}
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		idx=2;
		scanf("%d",&n);
		n=1<<n;
		for(int i=1;i<=n;i++)
			h[i]=-1;
		int u,v;
		for(int i=1;i<n;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v);add(v,u);
		}
		cnt=n;
		dfs(1,-1,n,0);
		puts("1");
		for(int i=1;i<=n;i++)
			printf("%d ",w1[i]);
		puts("");
		for(int i=1;i<n;i++)
			printf("%d ",w2[i]);
		puts("");
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值