Codeforces Round #658 (Div. 1)
题意:一个序列,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} 2n−x,如果遇到重复的,那么将其设定为miscolor。最后,还要验证一下是否找齐了n-y个miscolor。
#include
#include
#include
#include
#include
#include
#include
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;
}
题意:在一棵树上有一条蛇,这条蛇蛇头在s,蛇尾在t,询问这条蛇能不能掉头(蛇头在t,蛇尾在s)。
思路:
关键点:从这个点开始走,有三条完全不同的路径,长度大于n。
如果蛇头或蛇尾能到达一个关键点,那么就可以掉头。
如果能蛇头或蛇尾到达一个关键点,那么就可以到达这棵树上任意一个关键点。
综上,用树形DP找出所有关键点,然后以任意一个关键点为根,看蛇头能否走到根。具体方法为,蛇头向其最深的叶子节点走,然后蛇尾再向其最深的叶子节点走,循环往复。如果蛇头(尾)成为蛇尾(头)的祖先,则可以走到根;如果蛇头(尾)重复到达某一节点,那么永远不能走到根。
#include
#include
#include
#include
#include
#include
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;
}