CF 1381/1382 Codeforces Round #658 (Div. 1/2)

Codeforces Round #658 (Div. 1)

div1 C Mastermind

题意:一个序列,n个数,包含颜色的序号不超过n+1,求满足以下条件的序列是否存在。
1. 包含的颜色序号不超过n + 1
2. 有x个位置和原序列完全一样
3. 有y个位置包含原序列的颜色,但和原序列不一定一样。
思路
  有一种颜色多余,我们称之为miscolor。
  先看x个和原序列一样的位置,找数目最多的颜色。
  指定好这x个元素以后,如果剩余的最大的颜色数*2> n-x + n-y,那么不可能构造成功。
  否则,所有元素向后移动 n − x 2 \frac{n-x}{2} 2nx,如果遇到重复的,那么将其设定为miscolor。最后,还要验证一下是否找齐了n-y个miscolor。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int N = 2e5+10;
vector<int>pos[N],cnt[N];
int que[N], a[N], b[N];
int main()
{
	int T;
	scanf("%d", &T);
	while(T)
	{
		T--;
		int n, x, y;
		scanf("%d%d%d", &n,  &x, & y);
		for(int i = 1; i <= n + 1; i++)
		{
			pos[i].clear();
			cnt[i].clear();
			a[i] = 0;
		}
		for(int i = 1; i <= n; i++)
		{
			scanf("%d", &b[i]);
			pos[b[i]].push_back(i);
		}
		int miscolor;
		for(int i = 1; i <= n + 1; i++)
		{
			if(pos[i].size())	cnt[pos[i].size()].push_back(i);
			else	miscolor = i;
		}
		int now = n;
		while(cnt[now].empty())	now--;
		for(int i = 1; i <= x; i++)//x个和原来一样的位置
		{
			while(cnt[now].empty())	now--;
			int nowx = cnt[now][cnt[now].size() - 1];
			cnt[now].pop_back();
			a[pos[nowx][pos[nowx].size() - 1]] = b[pos[nowx][pos[nowx].size() - 1]];
			pos[nowx].pop_back();
			cnt[now - 1].push_back(nowx); 
		}
		while(now && cnt[now].empty())	now--;
		if(now * 2 > n * 2 - x - y)	
		{
			printf("NO\n");
			continue;
		}
		int tail = 0;
		for(int i = 1; i <= n + 1; i++)
			if(pos[i].size())
				for(int j = 0; j < pos[i].size(); j++)
					que[++tail] = pos[i][j];
		 int ot = n - y;
		 for(int i = 1; i <= tail; i++)
		 {
		 	int to = i + (n - x) / 2;
		 	if(to > n - x)	to -= n-x;
		 	a[que[i]] = b[que[to]];
		 	if(a[que[i]] == b[que[i]])
		 	{
		 		a[que[i]] = miscolor;
		 		ot--;
		 	}
		 }
		 for(int i = 1; i <= tail && ot; i++)
		{
			if(a[que[i]] != miscolor)
			{
				a[que[i]] = miscolor;
				ot--;
			}
		}
		printf("YES\n");
		for(int i = 1; i <= n; i++)	printf("%d ",a[i]);
		printf("\n");
	}
	return 0;
}

div1 D The Majestic Brown Tree Snake

题意:在一棵树上有一条蛇,这条蛇蛇头在s,蛇尾在t,询问这条蛇能不能掉头(蛇头在t,蛇尾在s)。
思路
  关键点:从这个点开始走,有三条完全不同的路径,长度大于n。
  如果蛇头或蛇尾能到达一个关键点,那么就可以掉头。
  如果能蛇头或蛇尾到达一个关键点,那么就可以到达这棵树上任意一个关键点。
  综上,用树形DP找出所有关键点,然后以任意一个关键点为根,看蛇头能否走到根。具体方法为,蛇头向其最深的叶子节点走,然后蛇尾再向其最深的叶子节点走,循环往复。如果蛇头(尾)成为蛇尾(头)的祖先,则可以走到根;如果蛇头(尾)重复到达某一节点,那么永远不能走到根。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e5 + 10;
struct EDGE{
	int to, nxt;
	EDGE(int x = 0, int y = 0){to = x; nxt = y;}
}edge[N * 2];
int f[22][N], dep[N], maxdep[N], vist[N], t[N], cnt[N], maxdep1[N], maxdep2[N], maxlen1[N], maxlen2[N];
int a, b, tot, k, rt, flag;
void memclear(int n)
{
	for(int i = 1; i <= n; i++)
		t[i] = 0, vist[i] = 0, cnt[i] = 0;
	tot = 0;
}
void addedge(int x, int y)
{
	edge[++tot] = EDGE(y, t[x]);
	t[x] = tot;
}
void dfs1(int x, int fa) //dfs确定深度
{
	for(int p = t[x]; p; p = edge[p].nxt)
	{
		int y = edge[p].to;
		if(y != fa)
		{
			dep[y] = dep[x] + 1;
			dfs1(y, x);
		}
	} 
}
void dfs2(int x, int fa)//树形DP求最长路径
{
	maxdep1[x] = x;
	maxdep2[x] = x;
	maxlen1[x] = 0;
	maxlen2[x] = 0;
	for(int p = t[x]; p; p = edge[p].nxt)
	{
		int y = edge[p].to;
		if(y != fa)
		{
			dfs2(y, x);
			if(maxlen1[y] + 1 > maxlen2[x])
			{
				maxdep2[x] = maxdep1[y];
				maxlen2[x] = maxlen1[y] + 1;
			}
			if(maxlen1[x] < maxlen2[x])
			{
				swap(maxdep1[x], maxdep2[x]);
				swap(maxlen1[x], maxlen2[x]);
			}
			if(maxlen1[y] + 1 >= k)	cnt[x] ++;
		}
	}
}
void dfs3(int x, int fa) //DP换根
{
	for(int p = t[x]; p; p = edge[p].nxt)
	{
		int y = edge[p].to;
		if(y != fa)
		{
			if(maxdep1[x] == maxdep1[y])
			{
				if(maxlen2[x] + 1 >= k)
					cnt[y]++;
				if(maxlen2[x] + 1 > maxlen2[y])
				{
					maxlen2[y] = maxlen2[x] + 1;
					maxdep2[y] = maxdep2[x];
				}
			}
			else
			{
				if(maxlen1[x] + 1 >= k)
					cnt[y]++;
				if(maxlen1[x] + 1> maxlen2[y])
				{
					maxlen2[y] = maxlen1[x] + 1;
					maxdep2[y] = maxdep1[x];
				}
			}
			if(maxlen1[y] < maxlen2[y])
			{
				swap(maxdep1[y], maxdep2[y]);
				swap(maxlen1[y], maxlen2[y]);
			}
			dfs3(y, x);
		}
	}
 } 
int judge(int n)
{
	for(int i = 1; i <= n; i++)
		if(cnt[i] >= 3)
		{
			rt = i;
			return 1;
		}
	return 0;
}
void dfs4(int x, int fa) //求每一个点最深的叶子节点
{
	maxdep[x] = x;
	for(int p = t[x]; p; p = edge[p].nxt)
	{
		int y = edge[p].to;
		if(y != fa)
		{
			f[0][y] = x;
			dep[y] = dep[x] + 1;
			dfs4(y, x);
			if(dep[maxdep[y]] > dep[maxdep[x]])
				maxdep[x] = maxdep[y];
		}
	}
}
int get_lca(int x, int y)
{
	if(dep[y] < dep[x])	swap(x, y);
	for(int i = 20; i >= 0; i--)
		if(dep[y] - dep[x] >= (1 << i))
			y = f[i][y];
	if(x == y)	return x;
	for(int i = 20; i >= 0; i--)
		if(f[i][x] != f[i][y])
		{
			x = f[i][x];
			y = f[i][y];
		}
	return f[0][x];
}
int findfa(int x, int len) //向上跳len步
{
	for(int i = 20; i >= 0; i--)
		if(len >= (1 << i))
		{
			x = f[i][x];
			len -= (1 << i);
		}
	return x;
}
int solve(int n) //蛇头蛇尾交替向叶子走
{
	if(k == a || k == b)	return 1;
	for(int i = 1; i <= n; i++)
	{
		int x = maxdep[a];
		if(vist[x])	return 0;
		if(dep[x] - dep[a] >= dep[b] - dep[k])	return 1;
		b = findfa(b, dep[x] - dep[a]);
		a = x;
		vist[x] = 1;
		swap(a, b);
	}
	return 0;
}
int main()
{
	int T;
	scanf("%d", &T);
	if(T >= 100)
		flag = 1;
	while(T)
	{
		T--;
		int n, x, y;
		scanf("%d%d%d", &n, &a, &b);
		memclear(n);
		for(int i = 1; i < n; i++)
		{
			scanf("%d%d", &x, &y);
			addedge(x, y);
			addedge(y, x);
		}
		dep[a] = 0;
		dfs1(a, 0);
		k = dep[b];
		dfs2(a, 0);
		dfs3(a, 0);
		if(!judge(n))
		{
			printf("NO\n");
			continue;
		}
		for(int i = 0; i <= 20; i++)
			for(int j = 1; j <= n; j++)
				f[i][j] = 0;
		dep[rt] = 0;
		dfs4(rt, 0);
		for(int i = 1; i <= 20; i++)
			for(int j = 1; j <= n; j++)
				f[i][j] = f[i - 1][f[i - 1][j]];
		k = get_lca(a, b);
		if(solve(n))	printf("YES\n");
		else	printf("NO\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值