Codeforces Round 992 (Div. 2) 题解(A~D)
广告:starrycoding 9 折优惠码:
FV7B04LL
E 有空再补
构造场, 构造低手掉分.
A
不记得为什么卡了, 居然写了 7min.
思路
n≤102, 甚至可以使用 n3 算法.
枚举每个 ai, 判断 ai 与 {a1,a2,…,ai−1}∪{ai+1,ai+2,…,an} 组合后, 是否可以都不被 k 整除.
复杂度 O(n2).
代码
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)⋅2) 的位置, 才能满足 l∼r 半数有 1,
3 最多放置在 10((4+1)⋅2).
设当前 i 的总数为 cnt.
可以得到, 第 i 个 1, 可以放置在(cnt+1)⋅2 的位置.
那么根据上述模拟即可.
复杂度 O(logn)
代码
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,…,n−1,n, 满足条件.
要满足上述内容, 除了 n 之外的所有数, 两侧至少有一个 大于自己的数.
那么如果我们从 n开始放置:
n−1 可以放置在 n 在左右侧.
n−2 可以放在 整体 的左右, 但是不能方阵 n,n−1 之间, 因为会让 n−1 捕鱼 n 相邻, 就没有再比它大的数了.
那么从 n 放置到 1, 每次只能放置在整体的左右.
但是若是从 n 开始放置, 不好判断字典序顺序(看下述可得).
反向考虑, 从 1 开始.
下述内容画一画就很明显了
1 只能防止在最左侧/最右侧.
2 只能防止在 1 只内的最左/最右(1 在左时, 2 放在 1 由或 最右, 反之亦然. )
3 只能放在新区间的最左/最右.
那么总共有 2n−1 种放法.
如果 1 在最前, 字典序就是前 2n−1/2(也就是2n−2).
否则, 字典序就是 2n−1/2+1∼2n−1.
对于 2, 如果 2 在新区间最前, 其字典序在此范围的 1∼2n−3
即如果 1 在最前, 2 在最前的字典序范围 1∼2n−3.
如果 1 在最后, 2 在最后的字典序范围 2n−2+1∼2n−1.
又因为 2n≫sizeof(longlong), 所以只能选择别的方法表示.
根据上述分析, 一次可以排除掉整体区间的 一半, 那么多次就是典型的 log.
那么可以把 2n 和 k 表示为 n 和 logk
具体看代码吧.
复杂度
代码
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⋅n 个点.
先假设一条 5 条的链, 我们只放奇数, 那么:
是可以满足情况的.
但是若是点很少:
是放不下的.
再考虑花形:
三个点的链也属于花形.
整个图可以可以视作若干个上述小单元的组合(链和花).
每个小单元则只能使用 cnt点总数⋅2个数.
而2不能使用, 每个单元必然有一个数不能放置.
如果填入 1, 那么该单元就可以放满了:
那么从根节点出发, 每次优先放 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⋅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';
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具