【题解】AtCoder Beginner Contest 318(D - Ex)
赛时过了 A-G,Ex 仿佛猜到了结论但是完全不懂多项式科技,就炸了。
大家好像都秒了 A,B,C 就不写了。
D.General Weighted Max Matching
题目描述:
给你一个加权无向完全图,图中有 个顶点,编号从 到 。连接顶点 和 的 的权重为。
在以下条件下选择若干条边时,请找出所选边的最大可能总权重。
- 所选边的端点是成对不同的。
所有输入值均为整数。
题目分析:
一眼看过去这是一般图的最大权独立集,根本没啥多项式做法。
所以就直接爆搜的就好了,也就是对于每一个点找它匹配的点,需要注意的是可以有点不匹配边。
这样的复杂度就是 显然可以过。
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 20;
int n,match[N],d[N][N],ans,sum;
void dfs(int now){
if(now == n + 1){
ans = max(ans,sum);
return;
}
if(match[now]){
dfs(now+1);
return;
}
for(int i=now+1; i<=n; i++){
if(match[i]) continue;
match[now] = i,match[i] = now;
sum += d[now][i];
dfs(now+1);
match[now] = match[i] = 0;
sum -= d[now][i];
}
dfs(now+1);
}
signed main(){
scanf("%lld",&n);
for(int i=1; i<=n; i++){
for(int j=i+1; j<=n; j++){
scanf("%lld",&d[i][j]);
}
}
dfs(1);
printf("%lld\n",ans);
return 0;
}
E.Sandwiches
题目描述:
给你一个长度为 的正整数序列:.求满足下列所有条件的正整数三元组 的个数:
- ,
- ,
- .
题目分析:
看上去我们可以考虑枚举 然后看看有多少符合条件的 。
不妨假设 表示前 个数中有多少个等于 的数。
可以发现答案其实就是:
可以对于每一个 开一个桶维护位置,这样我们直接在每一个桶内维护就好了,这样 的限制就无了:
考虑每一个 会有 个 使得 可以产生这个贡献,所以这一部分对答案的贡献就是:
考虑每一个 会有 个 使得 可以产生这个贡献,所以这一部分对答案的贡献就是:
两部分求和就好。
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+5;
int a[N];
vector<int> v[N];
signed main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int n;scanf("%lld",&n);
for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
for(int i=1; i<=n; i++){
v[a[i]].push_back(i);
}
int ans = 0;
for(int i=1; i<=n; i++){
int len = v[i].size();
for(int j=0; j<v[i].size(); j++){
ans += -v[i][j] * (len - (j + 1));
ans += v[i][j] * j;
ans += j * (len - (j + 1));
ans += -j * j;
}
}
printf("%lld\n",ans);
return 0;
}
F.Octopus
题目描述:
在一条数线上有一个章鱼形机器人和 个宝物。第 个宝物 位于坐标 处。
机器人有一个头和条腿,条腿的长度为。
求满足如下要求的整数 的个数:
- 将头部置于坐标 处。
- 依次对 重复以下步骤:如果在距离头部 的距离内,即在满足 的坐标 处,有一件宝物尚未被抓取,则选择其中一件宝物并抓取。
题目分析:
首先考虑若给定 给怎么判断是否合法。
一个显然的贪心就是按与 的距离将宝藏排序,然后让第 长的腿去抓第 远的宝藏,若抓不到则无解。
类似扫描线的思想,我们会发现如果对于 任何一条腿都没有恰好碰到某一个宝藏,那么对于任意的 ,若 合法则 一定合法。
通俗点说就是,若当 为区间 内的某一个数时,腿 可以覆盖宝藏 ,那么无论 为 中的哪一个,腿 一直可以覆盖宝藏 。
考虑使用反证法证明,若腿 不可以覆盖宝藏 了那么意味着什么,那么意味着从能覆盖到不能覆盖,一定存在临界情况,即某一个腿使得 恰好碰到 ,因为我们 是均匀变化的,而当 都不存在任何一条腿恰好碰到任何一个宝藏,也就是我们不存在这种临界情况,即然不存在临界情况那么自然不会存在跨临界的情况,也就是腿覆盖宝藏的情况不会改变。
所以可以直接钦定某一条腿碰到某一个宝藏,这样可以得到 个 ,并且它们将区间分成了 段。
只需要对于每一个段里随便选一个数判断一下即可,然后再对于选出的 个 判断一下即可。
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
int n,tot,a[N],b[N],x[N],l[N],c[N];
bool chk(int k){
for(int i=1; i<=n; i++) a[i] = abs(x[i] - k);
for(int i=1; i<=n; i++) b[i] = l[i];
sort(a+1,a+n+1);
sort(b+1,b+n+1);
for(int i=1; i<=n; i++){
if(a[i] > b[i]) return false;
}
return true;
}
signed main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%lld",&n);
for(int i=1; i<=n; i++) scanf("%lld",&x[i]);
for(int i=1; i<=n; i++) scanf("%lld",&l[i]);
for(int j=1; j<=n; j++){
for(int i=1; i<=n; i++){
int k = x[i] + l[j];
c[++tot] = k;
k = x[i] - l[j];
c[++tot] = k;
}
}
sort(c+1,c+tot+1);
tot = unique(c+1,c+tot+1) - c - 1;
int ans = 0;
for(int i=1; i<tot; i++){
if(c[i] == c[i+1]) continue;
if(chk(c[i]+1)) ans += c[i+1] - c[i] - 1;
}
for(int i=1; i<=tot; i++) if(chk(c[i])) ++ans;
printf("%lld\n",ans);
return 0;
}
G.Typical Path Problem
题目描述:
问题陈述
给你一个简单连通的无向图,它有个顶点和条边。 的顶点和边分别编号为顶点、顶点、、顶点和边、边、、边,边连接顶点和。
在上还有不同的顶点。请判断是否有一条简单路径通过顶点连接顶点和。
什么是简单连通无向图?
当是简单且连通的无向图时,图被称为简单连通的无向图。
当 的边没有方向时,图 称为无向图。
当 不包含重边和自环时,图 是简单的。
当人们可以通过边在的所有顶点之间移动时,图是连通的。
什么是通过顶点的简单路径?
对于图上的顶点和,连接和的简单路径是一系列不同的顶点,使得 ,并且对于满足的每一个整数,在上有一条边连接顶点和。
当有一条 满足 时,称一条简单路径 经过顶点 满足 。
题目分析:
(官解竟然是网络流,充分说明科技的重要性,感觉震撼)
路径(必须)经过某个点的问题,显然想到圆方树,因为圆方树完美地保留了原图的必经性。
这个意思就是说圆方树路径上的圆点为两点路径的必经点,路径上的方点下的圆点就是两点路径的可经过的点。
所以只需要建出圆方树然后判断 路径上的圆点或方点下的圆点是否含有 即可。
代码:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int _ = 2e6 + 5;
int A,B,C,n, m, q, tp;
int cnt_node, cntn;
bool flag;
int dfn[_], low[_];
int dep[_], top[_], siz[_], hson[_], fa[_];
stack<int> s;
struct Graph
{
int tot, head[_], nxt[_ << 1], to[_ << 1];
void add(int u, int v)
{
nxt[++tot] = head[u];
to[tot] = v;
head[u] = tot;
nxt[++tot] = head[v];
to[tot] = u;
head[v] = tot;
}
} G, T;
void tarjan(int u)
{
dfn[u] = low[u] = ++cnt_node;
s.push(u);
for (int i = G.head[u], v; i; i = G.nxt[i])
{
v = G.to[i];
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u])
{
T.add(++cntn, u);
while (1)
{
int now = s.top();
s.pop();
T.add(cntn, now);
if (now == v)
break;
}
}
}
else
low[u] = min(low[u], dfn[v]);
}
}
void dfs1(int u, int d = 1)
{
siz[u] = 1;
dep[u] = d;
for (int i = T.head[u], v; i; i = T.nxt[i])
{
v = T.to[i];
if (dep[v])
continue;
fa[v] = u;
dfs1(v, d + 1);
siz[u] += siz[v];
if (siz[v] > siz[hson[u]])
hson[u] = v;
}
}
void chk(int now){
if(now <= n && now == B) flag = true;
else{
for(int i=T.head[now]; i; i=T.nxt[i]){
int to = T.to[i];
if(to == B) flag = true;
}
}
}
signed main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d%d",&n,&m);
scanf("%d%d%d",&A,&B,&C);
cntn = n;
for (int i = 1, u, v; i <= m; i++)
{
scanf("%d%d",&u,&v);
G.add(u, v);
}
tarjan(1);
dfs1(1);
flag = false;
while(A != C){
if(dep[A] < dep[C]) swap(A,C);
chk(A);
A = fa[A];
}
chk(A);
if(flag) printf("Yes\n");
else printf("No\n");
return 0;
}
Ex.Count Strong Test Cases
题目描述:
Snuke 遇到了以下问题。
给你的排列组合和。让我们如下建立一个有 个顶点和 条边的图。
- 依次为,画一条权重为的边,双向连接顶点和。
当删除一定数量的边以消除图中的循环时,求删除的边的总重量的最小值。
爱丽丝和鲍勃提出了以下解决方案。
爱丽丝: 初始化答案为。对于按此顺序排列的 ,如果连接顶点 和 的边包含在一个循环中,则删除该边并将其权重添加到答案中。
鲍勃: 将答案初始化为 。对于按此顺序排列的,如果连接顶点和的边包含在一个循环中,则删除该边并将其权重添加到答案中。
Snuke 意识到他们的解法都不正确,他想知道他们的解法都没有给出正确答案的输入数。
在 个可能的输入中,求爱丽丝和鲍勃的解都没有给出正确答案的输入个数(模为 )。
题目分析:
(这个大概就是 Atcoder 网站上的解法)
考虑答案可以容斥转化为所有的概率 - 两个全部正确的概率 - 恰好一个正确的概率。
首先若对于一个环最小的 满足 在环上不为这个环的最小值,则 Alice 会寄掉。
如果对于一个环最大的 满足 在环上不为这个环的最小值,则 Bob 会寄掉。
两个全部正确的概率,设为
而因为权值一定是一个排列,也就是不存在相等的情况,所以只要环不为自环则必然会至少寄一个,则显然
恰好一个正确的概率,设为
这个计算就可以考虑枚举 所在环的大小设为 ,则就有如下的式子:
上面的这个首先要知道一个定理就是:长度为 的置换,点 所在环的长度在 概率相等,所以上文的 的含义其实就是最小值在下标最大的/最小的位置,而后面的 相当于我们已经在 确定了谁寄,为了保证另一个不寄最小值只能在这一个位置。
对于 前面这一坨就是卷积,后面这一坨是半在线卷积,都是可以做的。
我们的答案显然就是 ,然后就做完了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律