2023牛客多校第三场 - A D I J
比赛地址:传送门
赛时过了3题AHJ,最后10分钟D出思路,但没时间了,赛后过
A 签到题
D 需要一点点思维
I LCA 问题,难题。。。但值得一做
J topo排序 + 思维
A World Fragments I
题意
给你一个二进制串 x 和 y,你可以取 x 某个数位上的数字 b,使得 \(x = x \pm b\),问你使得 x 变为 y 的最小操作次数
思路
简单签到题
- 当 \(x = y\) 时,\(ans = 0\)
- 当 \(x \ne y\) 时,当 \(x = 0\) 时 \(ans = 0\),反之 \(ans = | y - x |\)
代码
void solve(){
string a,b;
cin >> a >> b;
if(a == b) cout << 0;
else{
if(a == "0") cout << -1;
else{
ll x = 0, y = 0;
for(auto c : a){
x = (x << 1) + c - '0';
}
for(auto c : b){
y = (y << 1) + c - '0';
}
cout << abs(x - y);
}
}
return ;
}
D Ama no Jaku
题意
给你一个 \(n \times n\) 的矩阵,每个位置上的数字均为 0 或 1,你可以进行一种操作:选择某一行或者某一列使其 01 翻转。问你最少的操作次数,使得行表示的最小二进制数大于列表示 的最大二进制数,若无法实现,输出 -1。
思路
考虑后会发现,只有整个矩阵全 0 或者全 1 时,才有可能成立,那么就是判断给定的矩阵通过最少的操作次数使其变为全 0 矩阵或者全 1 矩阵。
正面想不容易,可以反面考虑,由全0或者全1矩阵变为给定矩阵所需的最小操作次数。那么我们可以发现,无论怎么行列变换,当仅进行列变换时,所有的行均相等,再进行行变换后,所有的行只存在两种情况,要么与第一行相等,要么与第一行01翻转后相等,所以存在性就利用这个判断即可。
最后是最小的操作次数,首先就是存在时,所有的字符串仅存在两种:s[0]和翻转s[0],那么,只需要统计各自的个数,再判断其列翻转为全1或者全0的次数加起来,取小即可。取小的话是先列翻转取小(判断由全1变来次数少还是全0变来次数少),再行翻转取小(判断行翻转应用于s[0]的次数少还是应用于翻转s[0]的次数少 )。
代码
//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long
using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*
*/
const int maxm = 2e5 + 5, inf = 0x3f3f3f3f, mod = 998244353;
void solve(){
int n;
cin >> n;
string s[2],t;
cin >> s[0];
s[1] = s[0];
ll ans = 0, c[2] ={0, 0};
for(int i = 0; i < n; ++ i){
if(s[0][i] == '1'){
s[1][i] = '0';
++ c[1];
}
else{
s[1][i] = '1';
++ c[0];
}
}
bool f = true;
int d[2] = {1, 0};
for(int i = 0; i < n - 1; ++ i){
cin >> t;
if(f){
if(t == s[0]){
++ d[0];
}else if(t == s[1]){
++ d[1];
}else{
f = false;
}
}
}
if(f) ans = min(c[0], c[1]) + min(d[0], d[1]);
else ans = -1;
cout << ans << '\n';
return ;
}
signed main(){
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int _ = 1;
// cin >> _;
while(_ --){
solve();
}
return 0;
}
I To the Colors of the Dreams of Electric Sheep
题意
有一个 n 个节点的树,每一个节点所包含的颜色均有二进制数 \(c_i\) 定义。现在给你 q 次询问,每次从 u 出发到 v,你的初始颜色可以自由选择。你可以花费 1 秒的时间移动到相邻的具有和你同色的节点,或者说该自己的颜色为所在节点的某种颜色。问你抵达所需花费的最小时间?无法抵达输出 -1
思路
思路来自懵哥
详见代码,这里仅做一点点解释。
数组 dep 和 Fa 维护 lca 最近公共祖先
col 维护每个节点每种颜色不变色最高能跳到何处,jmp 维护每个节点改变 2 的 k 次方中颜色能抵达的最高位置
dfs 函数预处理,lca 函数求最近公共祖先,calc计算最后的答案
代码
//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long
using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*
*/
const int maxm = 5e5 + 5, inf = 0x3f3f3f3f, mod = 998244353;
int n, q;
int jmp[21][maxm], dep[maxm], f[21][maxm], Fa[maxm];
int col[60][maxm]; //每个颜色能跳的最高的节点
ll a[maxm];
vector<int> ve[maxm];
void dfs(int x,int fa){
dep[x] = dep[fa] + 1;
Fa[x] = fa;
int up = -1;
//处理当前节点向上最多不变色走到哪里
for(int i = 0; i < 60; ++ i){
col[i][x] = col[i][fa];
if((a[x] >> i & 1) && col[i][x] == -1)
col[i][x] = x;
if(~a[x] >> i & 1)
col[i][x] = -1;
if(up == -1 || (col[i][x] != -1 && dep[up] > dep[col[i][x]]))
up = col[i][x];
}
//倍增处理变2的k次方色会跳到哪里
jmp[0][x] = up;//不变色到up
for(int i = 0; i <= 19; ++ i){
f[i + 1][x] = f[i][f[i][x]];//向上2的i + 1的父亲为自己向上2的i的父亲再向上i
if(jmp[i][x] != -1)
jmp[i + 1][x] = jmp[i][jmp[i][x]];//倍增向上跳
}
for(auto it : ve[x]){//dfs子树
if(it == fa) continue;
f[0][it] = x;
dfs(it, x);
}
return ;
}
int lca(int x, int y){//朴素的lca求最近公共祖先
if(dep[x] < dep[y]) swap(x, y);
for(int i = 20; i >= 0; -- i){
if(dep[f[i][x]] >= dep[y])
x = f[i][x];
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 calc(int x, int y){//计算最终答案
int L = lca(x, y);
int ans = dep[x] + dep[y] - dep[L] * 2;//跑的路径一定是在u,v和L之间的最短路径上
if(x == L) swap(x, y);
if(x == L) return 0;//两点均位与L处
if(y == L){//一点为祖先,另一点向上跑
for(int i = 20; i >= 0; -- i){
if(jmp[i][x] != -1 && dep[jmp[i][x]] > dep[L]){//x向上跑
ans += 1 << i;//预处理可知需要2的i次方步
x = jmp[i][x];
}
}
if(a[x] & a[Fa[x]]) return ans;//最后一步x可达
return -1;
}
//两点均向上跑
for(int i = 20; i >= 0; -- i){
if(jmp[i][x] != -1 && dep[jmp[i][x]] > dep[L]){
ans += 1 << i;
x = jmp[i][x];
}
}
for(int i = 20; i >= 0; -- i){
if(jmp[i][y] != -1 && dep[jmp[i][y]] > dep[L]){
ans += 1 << i;
y = jmp[i][y];
}
}
if(jmp[0][x] == -1 || jmp[0][y] == -1) return -1;//有某个点不可达
if(dep[jmp[0][x]] > dep[L] || dep[jmp[0][y]] > dep[L]) return -1;//某个点不可达
bool flag = false;//判断在最近祖先处是否需要改变颜色
for(int i = 0; i < 60; ++ i){
if(col[i][x] != -1 && col[i][y] != -1){
if(dep[col[i][x]] <= dep[L] && dep[col[i][y]] <= dep[L]) flag = true;
}
}
if(flag) return ans;
else return ans + 1;
}
void solve(){
cin >> n >> q;
mem(col, -1);
mem(jmp, -1);
for(int i = 1; i <= n; ++ i){
cin >> a[i];
}
for(int i = 1; i < n; ++ i){
int u, v;
cin >> u >> v;
ve[u].push_back(v);
ve[v].push_back(u);
}
dfs(1,0);//dfs 预处理lca
while(q--){
int u, v;
cin >> u >> v;
cout << calc(u, v) << '\n';
}
return ;
}
signed main(){
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int _ = 1;
// cin >> _;
while(_ --){
solve();
}
return 0;
}
J Fine Logic
题意
给你 n 个抽象物体,m 种两两胜利关系,让你找到最少的 1 ~ n 的排列的个数,使得所有的胜利关系均在至少一个排列中得到体现。
思路
不难想到最多 2 个排列,最少 1 个排列即可表示题意
n 个顶点,m 条有向边,若不成环 topo 排序即为答案,若成环,则输出 1 ~ n 和 n ~ 1 即可,因为这俩包含了所有的两两可能情况。如果说能有办法能找到别的处理环的办法,也不是不可以,但是这样的输出最优,但需要一点思维。
代码
//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long
using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*
*/
const int maxm = 1e6 + 5, inf = 0x3f3f3f3f, mod = 998244353;
vector<int> e[maxm];
vector<int> ans[2];
int n, m, k, in[maxm];
void solve(){
cin >> n >> m;
k = 1;
for(int i = 0; i < m; ++ i){
int u, v;
cin >> u >> v;
e[u].push_back(v);
++ in[v];
}
deque<int> q;
for(int i = 1; i <= n; ++ i){
if(in[i] == 0) q.push_back(i);
}
while(!q.empty()){
int t =q.front();
q.pop_front();
ans[0].push_back(t);
for(auto c : e[t]){
-- in[c];
if(in[c] == 0) q.push_back(c);
}
}
if(ans[0].size() != n){
k = 2;
ans[0].clear();
for(int i = 1; i <= n; ++ i){
ans[0].push_back(n - i + 1);
ans[1].push_back(i);
}
}
cout << k << '\n';
for(int i = 0; i < k; ++ i){
for(auto c : ans[i]){
cout << c << ' ';
}
cout << '\n';
}
return ;
}
signed main(){
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int _ = 1;
// cin >> _;
while(_ --){
solve();
}
return 0;
}
本文来自博客园,作者:Qiansui,转载请注明原文链接:https://www.cnblogs.com/Qiansui/p/17578088.html