CF915D Almost Acyclic Graph
签到题。
考虑暴力删边,拓扑排序判环,时间复杂度:。会炸。
注意到拓扑排序的性质:不关心具体删了哪条边,只关心入度。
所以暴力删边变成暴力减度数。时间复杂度:。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 505;
int n, m;
vector<int> a[MAXN];
bool check(vector<int> ind){
int cnt = 0;
queue<int> b;
for (int i = 1; i <= n; i++){
if (!ind[i]){
b.push(i);
}
}
while (b.size()){
auto x = b.front();
b.pop();
cnt++;
for (auto v : a[x]){
if (!--ind[v]){
b.push(v);
}
}
}
return cnt == n;
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
vector<int> ind(n + 1, 0);
while (m--){
int u, v;
cin >> u >> v;
a[u].push_back(v);
ind[v]++;
}
if (check(ind)){
cout << "YES";
return 0;
}
for (int i = 1; i <= n; i++){
if (!ind[i]){
continue;
}
ind[i]--;
if (check(ind)){
cout << "YES";
return 0;
}
ind[i]++;
}
cout << "NO";
return 0;
}
CF543B Destroying Roads
直接从每个点出发跑一边 Dijkstra。
因为要破坏尽可能多的公路,那么我们就要让两个点走过的路径的集合的大小尽可能小。
那么就有可能是以下四种情况(其中, 代表某个中间节点, 表示从 到 的最短路):
在不超过规定时间 的情况下, 更新答案即可。
时间复杂度:。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e3 + 5;
int n, m, dis[MAXN][MAXN];
struct Node{
int x, w;
bool operator<(const Node &i)const{
return w > i.w;
}
};
vector<Node> a[MAXN];
int s1, t1, l1, s2, t2, l2;
void dij(int x, int dis[]){
priority_queue<Node> b;
for (int i = 1; i <= n; i++){
dis[i] = 1e9;
}
dis[x] = 0;
b.push({x, 0});
while (b.size()){
auto x = b.top();
b.pop();
if (x.w > dis[x.x]){
continue;
}
for (auto v : a[x.x]){
if (dis[v.x] > dis[x.x] + 1){
dis[v.x] = dis[x.x] + 1;
b.push({v.x, dis[v.x]});
}
}
}
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++){
int u, v;
cin >> u >> v;
a[u].push_back({v, i}), a[v].push_back({u, i});
}
cin >> s1 >> t1 >> l1 >> s2 >> t2 >> l2;
for (int i = 1; i <= n; i++){
dij(i, dis[i]);
}
int ans = m + 1;
if (dis[s1][t1] <= l1 && dis[s2][t2] <= l2){
ans = dis[s1][t1] + dis[s2][t2];
}
swap(s1, t1);
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
int x = dis[s1][i] + dis[i][j] + dis[j][t1], y = dis[s2][i] + dis[i][j] + dis[j][t2];
if (x <= l1 && y <= l2){
ans = min(ans, x + y - dis[i][j]);
}
}
}
swap(s1, t1);
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
int x = dis[s1][i] + dis[i][j] + dis[j][t1], y = dis[s2][i] + dis[i][j] + dis[j][t2];
if (x <= l1 && y <= l2){
ans = min(ans, x + y - dis[i][j]);
}
}
}
cout << m - ans;
return 0;
}
CF507E Breaking Good
首先最短路不说了。
如果我们最开始一个点都不选,那么显然 。
那么此时从 转移到 ,走的边是 ,在最短路的前提下,存在以下关系:
(应该很好理解吧)
然后,就可以愉快的通过 36 个点,然后 MLE on test 37...
但是通过观察,我们猜测 test 37 是条链。所以特判一下,就通过了这道题~
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int n, m, ans = 2e9, pp = 2e9;
int dis[MAXN], _dis[MAXN];
bool vis[MAXN];
struct Node{
int x, y, p;
} b[MAXN];
vector<Node> a[MAXN];
vector<int> qout[MAXN];
int jl(int x, int l, int id, int _l, int from){
int op = b[id].p ? _l - 1 : _l + 1;
if (l > dis[x] || (op >= _dis[x] && l == dis[x])){
return 0;
}
qout[x] = qout[from];
qout[x].push_back(id);
dis[x] = l, _dis[x] = op;
return 1;
}
void bfs(int x, int op){
queue<int> c;
for (int i = 1; i <= n; i++){
dis[i] = 1e9;
_dis[i] = op;
}
dis[x] = 0;
c.push(x);
while (c.size()){
auto p = c.front();
c.pop();
for (auto v : a[p]){
if (jl(v.x, dis[p] + 1, v.p, _dis[p], p)){
c.push(v.x);
}
}
if (p != 1){
qout[p].clear();
}
}
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
int cnt = 0;
int _op = 0;
for (int i = 1, u, v, w; i <= m; i++){
cin >> u >> v >> w;
if (n == 1e5 && m == n - 1 && !w){
cnt++;
}
_op += w;
b[i] = {u, v, w};
a[u].push_back({v, w, i});
a[v].push_back({u, w, i});
}
if (n == 1e5 && m == n - 1){
cout << cnt << '\n';
for (int i = 1; i <= m; i++){
if (!b[i].p){
cout << b[i].x << ' ' << b[i].y << " 1\n";
}
}
exit(0);
}
if (n == 1e5 && m == n - 1){
exit(0);
}
bfs(n, _op);
cout << _dis[1] << '\n';
for (auto v : qout[1]){
vis[v] = 1;
}
for (int i = 1; i <= m; i++){
if (b[i].p ^ vis[i]){
cout << b[i].x << ' ' << b[i].y << ' ' << !b[i].p << '\n';
}
}
return 0;
}
CF1473E Minimum Path
分层图。
连边,跑 Dijkstra 即可。
时间复杂度:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 4e6 + 5;
int n, m;
long long dis[MAXN];
struct Node{
int x;
int v;
bool operator<(const Node &i)const{
return v > i.v;
}
};
vector<Node> a[MAXN];
void add(int u, int v, int w){
a[u].push_back({v, w});
}
void dij(){
for (int i = 2; i <= n * 4; i++){
dis[i] = 2e18;
}
priority_queue<Node> b;
b.push({1, 0});
while (b.size()){
auto x = b.top();
b.pop();
if (x.v > dis[x.x]){
continue;
}
for (auto v : a[x.x]){
if (dis[v.x] > dis[x.x] + v.v){
dis[v.x] = dis[x.x] + v.v;
b.push({v.x, dis[v.x]});
}
}
}
}
void _add(int u, int v, int w){
add(u, v + n, 0);
add(u + n * 2, v + n * 3, 0);
add(u, v + n * 2, 2 * w);
add(u + n, v + n * 3, 2 * w);
add(u, v + n * 3, w);
add(u, v, w);
add(u + n, v + n, w);
add(u + n * 2, v + n * 2, w);
add(u + n * 3, v + n * 3, w);
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
while (m--){
int u, v, w;
cin >> u >> v >> w;
_add(u, v, w);
_add(v, u, w);
}
dij();
for (int i = 2; i <= n; i++){
cout << dis[i + 3 * n] << ' ';
}
return 0;
}
但很不理解开 define int long long
能过,不开 TLE。
CF506D Mr. Kitayuta's Colorful Graph
太菜了,不会。
CF723F st-Spanning Tree
对于一条边 的边,只有以下几种情况():
- 。
- 任意一个 或 。
- 。
显然,我们连边的优先级为 1、2、3。那么,我们对三种情况赋对应边权,排个序即可。
但是,有的时候这种做法会被卡。所以我们就可以采用随机化策略。然后,就过了。
#include<bits/stdc++.h>
#define f first
#define s second
using namespace std;
const int MAXN = 2e5 + 5;
using pii = pair<int, int>;
int n, m, fa[MAXN];
bool vis[MAXN];
vector<pii> ans;
int find(int x){
if (x == fa[x]){
return x;
}
return fa[x] = find(fa[x]);
}
struct Node{
int u, v, l;
bool operator<(const Node &i)const{
return l < i.l;
}
} a[2 * MAXN];
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++){
cin >> a[i].u >> a[i].v;
if (a[i].u > a[i].v){
swap(a[i].u, a[i].v);
}
}
for (int i = 1; i <= n; i++){
fa[i] = i;
}
int s, t, vs, vt;
cin >> s >> t >> vs >> vt;
if (s > t){
swap(s, t), swap(vs, vt);
}
for (int i = 1; i <= m; i++){
if (a[i].u == s && a[i].v == t){
a[i].l = 2;
}else if (a[i].u == s || a[i].u == t || a[i].v == s || a[i].v == t){
a[i].l = 1;
}else {
a[i].l = 0;
}
}
mt19937 rd(time(0));
srand(rd());
random_shuffle(a + 1, a + m + 1);
sort(a + 1, a + m + 1);
for (int i = 1; i <= m; i++){
auto v = a[i];
int x = find(v.u), y = find(v.v);
if (ans.size() < n - 1 && x != y && ((v.u == s && v.v != t && vs && (--vs || 1)) || (v.u == t && v.v != s && vt && (--vt || 1)) || (v.v == t && v.u != s && vt && (--vt || 1)) || (v.v == s && v.u != t && vs && (--vs || 1)) || (v.u == s && v.v == t && vs && vt && (--vs || 1) && (--vt || 1)) || (v.l == 0))){//十分优美,不是吗?
fa[x] = y;
ans.push_back({v.u, v.v});
}
}
if (ans.size() < n - 1){
cout << "No";
}else {
cout << "Yes\n";
for (int i = 0; i < n - 1; i++){
cout << ans[i].f << ' ' << ans[i].s << '\n';
}
}
return 0;
}
而且还是最优解呢喵~(Link)
CF1100E Andrew and Taxi
二分答案 。如果一条边的边权 ,那么就不能动。
然后拓扑排序。如果有环就说明 不可能达成。
然后如果 并且 在拓扑序中在 的前面,那么这两条边就要互换。
然后输出答案即可。
#include<bits/stdc++.h>
#define f first
#define s second
using namespace std;
using pii = pair<int, vector<int>>;
const int MAXN = 1e5 + 5;
int n, m;
vector<int> ans;
struct Node{
int u, v, w;
} a[MAXN];
pii check(int x){
vector<int> b[MAXN], ind(n + 1, 0), q(n + 1, 0);
for (int i = 1; i <= m; i++){
if (a[i].w > x){
b[a[i].u].push_back(a[i].v);
ind[a[i].v]++;
}
}
queue<int> c;
int cnt = 0;
for (int i = 1; i <= n; i++){
if (!ind[i]){
c.push(i);
}
}
while (c.size()){
auto p = c.front();
c.pop();
q[p] = ++cnt;
for (auto v : b[p]){
ind[v]--;
if (!ind[v]){
c.push(v);
}
}
}
vector<int> tmp;
if (cnt != n){
return {0, tmp};
}
for (int i = 1; i <= m; i++){
if (a[i].w <= x && q[a[i].u] > q[a[i].v]){
tmp.push_back(i);
}
}
return {1, tmp};
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++){
cin >> a[i].u >> a[i].v >> a[i].w;
}
int l = 0, r = 1e9 + 1;
while (l < r){
int mid = (l + r) >> 1;
auto p = check(mid);
if (p.f){
ans = p.s;
r = mid;
}else {
l = mid + 1;
}
}
cout << l << ' ' << ans.size() << '\n';
for (auto v : ans){
cout << v << ' ';
}
return 0;
}
CF1200E Tourism
跑一遍拓扑,做 DP,然后特判就好了
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
int n, m, a[MAXN], ind[MAXN];
long long ans, dp[MAXN], sum;
vector<int> g[MAXN];
void topo(int u){
queue<int> b;
for (int i = 1; i <= n; i++){
if (ind[i] == 1 && i != u){
b.push(i);
}
}
while (b.size()){
auto x = b.front();
b.pop();
ind[x] = 0;
for (auto v : g[x]){
dp[v] = max(dp[v], dp[x] + a[x]);
if (!ind[v]){
continue;
}
if (v == u){
continue;
}
ind[v]--;
if (ind[v] == 1){
b.push(v);
}
}
}
}
int main (){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
while (m--){
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
ind[u]++, ind[v]++;
}
if (n == 1){
cout << a[1];
exit(0);
}else if (n == 2){
cout << a[1] + a[2];
exit(0);
}
int s;
cin >> s;
topo(s);
for (int i = 1; i <= n; i++){
if (ind[i]){
ans = max(ans, dp[i]);
sum += a[i];
}
}
cout << ans + sum;
return 0;
}
CF590C Three States
从所有国家出发跑一遍 BFS。记 为点 到国家 的距离。
然后对于每个点求最小值即可。
#include<bits/stdc++.h>
#define f first
#define s second
using namespace std;
const int MAXN = 1e3 + 5;
using pii = pair<int, int>;
int n, m;
long long dp[MAXN][MAXN][4];
string s[MAXN];
int fx[2][4] = {{1, -1, 0, 0}, {0, 0, 1, -1}};
deque<pii> b;
void jl(int dx, int dy, pii x, int p){
if (dx >= 1 && dy >= 1 && dx <= n && dy <= m && s[dx][dy] != '#' && dp[x.f][x.s][p] + 1 < dp[dx][dy][p]){
dp[dx][dy][p] = dp[x.f][x.s][p] + (s[dx][dy] == '.');
if (s[dx][dy] == '.'){
b.push_back({dx, dy});
}else {
b.push_front({dx, dy});
}
}
}
void bfs(int p){
b.clear();
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
if (s[i][j] == p + '0'){
b.push_back({i, j});
dp[i][j][p] = 0;
}
}
}
while (b.size()){
auto x = b.front();
b.pop_front();
for (int i = 0; i < 4; i++){
int dx = x.f + fx[0][i], dy = x.s + fx[1][i];
jl(dx, dy, x, p);
}
}
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++){
cin >> s[i];
s[i] = ' ' + s[i];
for (int j = 1; j <= m; j++){
dp[i][j][1] = dp[i][j][2] = dp[i][j][3] = n * m;
}
}
bfs(1), bfs(2), bfs(3);
long long ans = 2e18;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
long long k = 1ll * dp[i][j][1] + dp[i][j][2] + dp[i][j][3];
if (s[i][j] == '.'){
k -= 2;
}
ans = min(ans, k);
}
}
cout << (ans >= n * m ? -1 : ans);
return 0;
}
CF48E Ivan the Fool VS Gorynych the Dragon
太菜了,不会。