Codeforces Round 992 (Div. 2) 题解(A~D)


广告:starrycoding \(9\) 折优惠码: FV7B04LL

\(E\) 有空再补
构造场, 构造低手掉分.

A

不记得为什么卡了, 居然写了 \(7 \min\).

思路

\(n \le 10^2\), 甚至可以使用 \(n^3\) 算法.
枚举每个 \(a_i\), 判断 \(a_i\)\(\{a_1,a_2,\ldots,a{i-1}\} \cup \{a_{i+1},a_{i+2},\ldots,a_n\}\) 组合后, 是否可以都不被 \(k\) 整除.

复杂度 \(O(n^2)\).

代码

void func(void)
{
	int n,k;
	cin >> n >> k;
	vector<int> a(n+1);
	for(int i=1;i<=n;++i)	cin >> a[i];
	for(int i=1;i<=n;++i)
	{
		int cnt = 0;
		for(int j=1;j<=n;++j)
		{
			if(i == j)	continue;
			int X = abs(a[i]-a[j]);
			if(X % k != 0)	cnt ++;
		}
		if(cnt == n-1)
		{
			cout << "YES\n" << i << '\n';
			return ;
		}
	}
	cout << "NO\n";
}

B

思路

对于操作 \(2\), 每次操作, 若是区间内有 超过一半(向上取整), 的位置有 \(1\), 那么区间内所有数都为 \(1\).

起始必须放置一个 \(1\).
那么我们从 \(1\) 开始放置(俺寻思, 事实上放在中间, 左右轮流放不会影响结果, 不做证明了),

\(1\) 放置后, \(2\) 最多放置在 \(4((1+1) \cdot 2)\) 的位置, 才能满足 \(l \sim r\) 半数有 \(1\),
\(3\) 最多放置在 \(10((4+1) \cdot 2)\).

设当前 \(i\) 的总数为 \(cnt\).
可以得到, 第 \(i\)\(1\), 可以放置在\((cnt+1) \cdot 2\) 的位置.

那么根据上述模拟即可.

复杂度 \(O(\log n)\)

代码

void func(void)
{
	int n;
	cin >> n;
	int X = 1,cnt = 0;
	while(X < n)
	{
		X = (X+1)*2;
		cnt ++;
	}
	cout << cnt+1 << '\n';
}

C

赛时没战意, 这题都没出.
这题不是很好讲, 需要结合图推.

思路

不递减排列是满足条件的.
\(1,2,3,\ldots,n-1,n\), 满足条件.

\[\begin{aligned} S(p) = &\quad \ 1+2+\ldots + n-3 + n-2 + n-1\\ &+ 1 + 2 + \ldots + n-3 + n-2\\ &+ 1 + 2 + \ldots + n-3 \\ &\ \ \vdots \\ &+ 1 + 2 \\ &+ 1\\ \end{aligned} \]

要满足上述内容, 除了 \(n\) 之外的所有数, 两侧至少有一个 大于自己的数.

那么如果我们从 \(n\)开始放置:
\(n-1\) 可以放置在 \(n\) 在左右侧.
\(n-2\) 可以放在 整体 的左右, 但是不能方阵 \(n, n-1\) 之间, 因为会让 \(n-1\) 捕鱼 \(n\) 相邻, 就没有再比它大的数了.

那么从 \(n\) 放置到 \(1\), 每次只能放置在整体的左右.

但是若是从 \(n\) 开始放置, 不好判断字典序顺序(看下述可得).

反向考虑, 从 \(1\) 开始.
下述内容画一画就很明显了
\(1\) 只能防止在最左侧/最右侧.
\(2\) 只能防止在 \(1\) 只内的最左/最右(\(1\) 在左时, \(2\) 放在 \(1\) 由或 最右, 反之亦然. )
\(3\) 只能放在新区间的最左/最右.

那么总共有 \(2^{n-1}\) 种放法.

如果 \(1\) 在最前, 字典序就是前 \(2^{n-1}/2\)(也就是\(2^n-2\)).
否则, 字典序就是 \(2^{n-1}/2+1 \sim 2^{n-1}\).

对于 \(2\), 如果 \(2\) 在新区间最前, 其字典序在此范围的 \(1\sim 2^{n-3}\)
即如果 \(1\) 在最前, \(2\) 在最前的字典序范围 \(1 \sim 2^{n-3}\).
如果 \(1\) 在最后, \(2\) 在最后的字典序范围 \(2^{n-2}+1 \sim 2^{n-1}\).

又因为 \(2^n \gg sizeof(long long)\), 所以只能选择别的方法表示.

根据上述分析, 一次可以排除掉整体区间的 一半, 那么多次就是典型的 \(\log\).
那么可以把 \(2^n\)\(k\) 表示为 \(n\)\(\log{k}\)

具体看代码吧.

复杂度

代码

int qpow(int a,int b)// 快速幂
{
	int res=1;
	while(b)
	{
		if(b&1)res=res*a;
		a=a*a;
		b=b>>1;
	}
	return res;
}

void func(void)
{
	int n,k;
	cin >> n >> k;
	vector<int> a(n+1);
	int l = 1,r = n;
	if(log2(k) > n-1)// c++ 内置log_2函数
	{
		cout << -1 << '\n';
		return ;
	}
	for(int i=1;i<=n-1;++i)
	{
		if(!k || log2(k) <= n-i-1)
		{
			a[l ++] = i;
		}
		else
		{
			a[r --] = i;
			k -= qpow(2,n-i-1);// 能够减去的数小于 10^12
		}
	}
	a[r --] = n;
	for(int i=1;i<=n;++i)	cout << a[i] << ' ';
	cout << '\n';
}

D

沟槽的领接表, 太久没写, 整体clear tle了, 多浪费 \(1h\), 但是多了一种思路.
一题三解, 真有我的.
前置知识: dfs, 领接表.
思路二需要: 埃氏筛.
思路三需要 set, set.lower_bound().

三解太耗精力, 之后可能会补充吧.

解法 \(1\)

思路

比较俺寻思, 但是感觉更好理解.

  • 要求相邻两数差不为质数, 偶数 除了\(2\), 都不为质数.
  • 总共只能使用 \(2\cdot n\) 个点.

先假设一条 \(5\) 条的链, 我们只放奇数, 那么:
alt text
是可以满足情况的.

但是若是点很少:
alt text
是放不下的.

再考虑花形:
alt text
三个点的链也属于花形.

整个图可以可以视作若干个上述小单元的组合(链和花).

每个小单元则只能使用 \(cnt_{点总数}\cdot 2\)个数.

\(2\)不能使用, 每个单元必然有一个数不能放置.

如果填入 \(1\), 那么该单元就可以放满了:
alt text

那么从根节点出发, 每次优先放 \(1\), 其次放差 \(2\) 以外同奇偶性的数.

本质还是解法\(1\), 只是实现不同.

代码

void dfs(int p,int lp)
{
	if(a[p].size() <= 1)	return ;
	int cnt = a[p].size() - 1;
	stack<int> X;
	if(ans[p]&1)
	{
		if(st2.lower_bound(ans[p]+1) != st2.end() && *st2.lower_bound(ans[p]+1) == ans[p]+1)
		{
			X.push(ans[p]+1);
			st2.erase(ans[p]+1);
		}
		vector<int> stp;
		while(X.size() != cnt)
		{
			int temp = *st1.begin();	st1.erase(temp);
			if(temp != ans[p] + 2 && temp != ans[p]-2)	X.push(temp); 
			else	stp.push_back(temp);
		}
		for(auto &i : stp)	st1.insert(i);
	}
	else
	{
		if(st1.lower_bound(ans[p]+1) != st1.end() && *st1.lower_bound(ans[p]+1) == ans[p]+1)
		{
			X.push(ans[p]+1);
			st1.erase(ans[p]+1);
		}
		vector<int> stp;
		while(X.size() != cnt)
		{
			int temp = *st2.begin();	st2.erase(temp);
			if(temp != ans[p] + 2 && temp != ans[p]-2)	X.push(temp);
			else	stp.push_back(temp);
		}
		for(auto &i : stp)	st2.insert(i);
	}
	for(auto &i : a[p])
	{
		if(i == lp)	continue;
		ans[i] = X.top(); 
		X.pop();
		
	}
	for(auto &i : a[p])
	{
		if(i == lp)	continue;
		dfs(i,p);
	}
}

void func(void)
{
	int n;
	cin >> n;
	for(int i=2;i<=n<<1;++i)
	{
		(i&1) == 1 ? st1.insert(i) : st2.insert(i);
	}
	for(int i=1;i<=n;++i)	a[i].clear();
	int x,y;
	for(int i=1;i<n;++i)
	{
		cin >> x >> y;
		a[x].push_back(y);
		a[y].push_back(x);
	}
	a[1].push_back(0);
	ans[1] = 1;
	dfs(1,0);
	for(int i=1;i<=n;++i)	cout << ans[i] << ' ';
	cout << '\n';
}

解法 \(2\)

从此视频学习: Codeforces Round 992 (Div. 2) 题目讲解 ABCDE (CF2040)

两个解法一直在 \(tle\), 只能找了题解.

思路

和解法 \(1\) 类似, 每个小单元最多使用 \(2\cdot k\) 个数. 而用 \(1\)\(2\), 同奇偶性的数刚好在范围内.

代码

int X;
vector<int> a[N],ans(N);

void dfs(int p,int lp)
{	
	ans[p] = X --;
	int cnt = 0;
	for(auto &i : a[p])
	{
		if(i == lp)	continue;
		if(ans[p]-X == 2) X -= 2;
		else if(ans[p]-X != 1 && (ans[p]-X)&1)	X --;
		cnt ++;
		dfs(i,p);
	}
	int len = a[p].size();
}

void func(void)
{
	int n;
	cin >> n;
	for(int i=1;i<=n;++i)	a[i].clear();
	int x,y;
	for(int i=1;i<n;++i)
	{
		cin >> x >> y;
		a[x].push_back(y);
		a[y].push_back(x);
	}
	X = n<<1;
	dfs(1,0);
	for(int i=1;i<=n;++i)	cout << ans[i] << ' ';
	cout << '\n';
}

解法 \(3\)

起始思路, 根本没想到是领接表问题.

思路

既然每条边差都为非素数, 那么dfs到每个点, 给子节点赋值 \(+\) 非素数即可. 复杂度懒得证明了.

代码

#include<bits/stdc++.h>
#define Start cin.tie(0), cout.tie(0), ios::sync_with_stdio(false)
#define PII pair<int,int> 
#define x first
#define y second
#define ull unsigned long long
#define ll long long
using namespace std;

const int M = 1000000007;
const int N = 2e5 + 10;

int len;
bitset<N<<1> vis;
vector<int> v(1,1),pri,a[N],ans(N);

void func(void);

void dfs(int p,int lp)
{
	int X = 0;
	for(auto &i : a[p])
	{
		if(i == lp)	continue;
		while(vis[ans[p]+v[X]])	X ++;
		ans[i] = ans[p] + v[X];
		vis[ans[i]] = true;
		dfs(i,p);
	}
}

signed main(void)
{
	Start;
	int n = N<<1;
	for(int i=2;i<=n;++i)
	{
		if(!vis[i])	pri.push_back(i);
		for(int j=0;pri[j]<=n/i;++j)
		{
			v.push_back(pri[j] * i);
			vis[pri[j] * i] = true;
			if(i % pri[j] == 0)	break;
		}
	}
	len = v.size();
	sort(v.begin(),v.end());
	int _ = 1;
	cin >> _;
	while(_--)	func();
	return 0;
}

void func(void)
{
	int n;
	cin >> n;
	vis.reset();
	for(int i=1;i<=n;++i)	a[i].clear();
	int x,y;
	for(int i=1;i<n;++i)
	{
		cin >> x >> y;
		a[x].push_back(y);
		a[y].push_back(x);
	}
	ans[1] = 1;
	dfs(1,0);
	for(int i=1;i<=n;++i)	cout << ans[i] << ' ';
	cout << '\n';
}
posted @ 2024-12-10 22:39  zerocloud01  阅读(205)  评论(0)    收藏  举报