比赛链接:
https://ac.nowcoder.com/acm/contest/31454
A.So I'll Max Out My Constructive Algor...
题意:
给定一个 \(n * n\) 的迷宫,每个点有一个高度,选择一个点开始,遍历所有点,要求这条路径满足所有点仅且只能遍历一次,同时这条路径向上走(即从高度的地方走到高度高的地方)的次数小于等于向下走的次数。
思路:
随便找一条遍历所有点的路径,如果满足条件则输出,否则反向输出即可。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve(){
int n;
cin >> n;
vector < vector <int> > a(n, vector<int>(n));
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n; j ++ )
cin >> a[i][j];
vector <int> path;
for (int i = 0; i < n; i ++ ){
if (i & 1){
for (int j = 0; j < n; j ++ )
path.push_back(a[i][j]);
}
else{
for (int j = n - 1; j >= 0; j -- )
path.push_back(a[i][j]);
}
}
int cnt = 0;
for (int i = 1; i < path.size(); i ++ ){
if (path[i - 1] < path[i]) cnt -- ;
else cnt ++ ;
}
if (cnt < 0) reverse(path.begin(), path.end());
for (int i = 0; i < path.size(); i ++ )
cout << path[i] << " \n"[i == path.size() - 1];
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
C.Laser Trap
题意:
在一个坐标系中已知 \(n\) 个点,任意两点间有一条线,从(0,0)出发,想到正无穷去,不能经过任何线或者点,问最少删除几个点才能到正无穷。
思路:
容易发现,将一个方向的半圆点保存之后(即将其它点删除),就可以逃出去了。
将所有坐标的反正切值进行排序,通过双指针去寻找最小值。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const long double pi = acosl(-1);
void solve(){
int n;
cin >> n;
vector<long double> a(2 * n);
for (int i = 0; i < n; i ++ ){
long double x, y;
cin >> x >> y;
a[i] = atan2l(y, x);
}
sort(a.begin(), a.begin() + n);
for (int i = 0; i < n; i ++ )
a[i + n] = a[i] + 2 * pi;
int ans = n;
for (int i = 0, j = 0; i < n; i ++ ){
while(j < 2 * n && a[j] - a[i] < pi)
j ++ ;
ans = min(ans, j - i - 1);
}
cout << ans << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T = 1;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
F.Sandpile on Clique
题意:
有一个完全图,每个点上有一个权值,当某个点权值大于与它相连的边时,它可以失去与边数相同的权值,然后给每一个连通的点分配 1 的权值,问图上每个点点的权值最后会不会趋于一个值不变,如果会,输出最后每一个点的权值,否则输出 "Recurrent"。
思路:
如果会趋于一个值,最多不超过 \(n - 2\) 次分配,如果超过了,说明会有一个新的点可以进行分配了,那么就会无限分配下去。
通过优先队列,每次将权值最大的那个点进行分配即可。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n;
cin >> n;
priority_queue<pair<int, int>> q;
vector <int> a(n);
for (int i = 0; i < n; i ++ ){
cin >> a[i];
q.push({a[i], i});
}
for (int i = 0; i < n - 1; i ++ ){
auto [x, id] = q.top();
q.pop();
if (x + i < n - 1){
for (int j = 0; j < n; j ++ )
cout << a[j] + i << " \n"[j == n - 1];
return 0;
}
a[id] -= n;
q.push({a[id], id});
}
cout << "Recurrent\n";
return 0;
}
K.Link-Cut Tree
题意:
\(n\) 个点 \(m\) 条边的无向图,第 \(i\) 条边的长度为 \(2^i\),找到一个长度最小的环,从小到达输出构成这个环的边。
思路:
根据贪心的思路,依次向图中加入边,当出现一个环的时候,这个环就是长度最小的那个,判断环可以通过并查集去做,找环可以通过拓扑。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
struct dsu{
int n;
vector <int> p;
dsu(int n) : n(n){
p.resize(n + 1);
iota(p.begin(), p.end(), 0);
}
int get(int x){
return (x == p[x] ? x : (p[x] = get(p[x])));
}
void unite(int x, int y){
x = get(x);
y = get(y);
if (x < y) swap(x, y);
if (x != y){
p[x] = y;
}
}
};
void solve(){
int n, m;
cin >> n >> m;
vector < array<int, 2> > e(m);
for (int i = 0; i < m; i ++ )
cin >> e[i][0] >> e[i][1];
dsu d(n);
vector < vector < pair<int, int> > > G(n + 1);
vector <int> deg(n + 1);
set <int> s;
auto topo = [&](){
queue <int> q;
for (int i = 1; i <= n; i ++ )
if (deg[i] == 1)
q.push(i);
while(!q.empty()){
int u = q.front();
q.pop();
for (auto [v, id] : G[u]){
if (s.count(id)) s.erase(id);
deg[u] -- ;
if ( -- deg[v] == 1){
q.push(v);
}
}
}
vector <int> ans;
for (auto x : s)
ans.push_back(x);
for (int j = 0; j < ans.size(); j ++ )
cout << ans[j] << " \n"[j == ans.size() - 1];
};
for (int i = 0; i < m; i ++ ){
auto [u, v] = e[i];
G[u].push_back({v, i + 1});
G[v].push_back({u, i + 1});
deg[u] ++ ;
deg[v] ++ ;
s.insert(i + 1);
if (d.get(u) == d.get(v)){
topo();
return;
}
d.unite(u, v);
}
cout << "-1\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}