2021CCPC - 桂林
大晚上看完EDG比赛就立马睡了,醒来状态还行,煎饼果子也不错,豆奶很好喝。()
A - A Hero Named Magnus
明天是周一,你很难受
因为熬夜看比赛
中国队输了
你还得上班
而猛犸他很开心
因为猛犸他不上ban
就,(2 imes x-1),签完了。(此时我甚至刚敲完文件头)
I - PTSD
就很神奇的是我还没读懂题目意思竹鱼大人直接拿了机子敲了就a了我都没法怎么说这道题因为到现在我还没读完这道题哈哈哈哈。
D - Assumption is All You Need
首先是我们推了个结论,一定从最大的数字开始调位置,每次找到调整区间内最大的值,然后与它进行交换,重复操作直至归位或无法操作。因为这样操作可以让大的数字尽可能在前面获得更多交换的可能。这种情况下,最坏情况(降序变成升序)交换次数恰为(n(n-1)/2),当时就直接冲了。
然后队友写了一发,大致就是记录数字位置,按照数字从大到小遍历数字位置来确认交换顺序,改了两次提了两发wa。当时也看不出能改什么地方了,我就想把机子抢过来边叉边重开。用了类似单调队(虽然但是,真的不需要啥单调队列,我只是最近学dp优化看了单调队列觉得像就写了)来找到交换序列,然后模拟交换并记录位置,前前后后乱七八糟的输出整个人都很乱。(当时手里还捏着道G没有解,因为对于现有算法没法叉掉,并且没有更好的新算法来解决)改完之后真的抱着搏命心态交了,真的过了。就心情挺复杂的。
#include <bits/stdc++.h>
#define ll long long
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define pb push_back
#define ppb pop_back
using namespace std;
const int maxn = 2100;
int m[2100];
int A[maxn];
int B[maxn];
int f1[maxn];
int f2[maxn];
vector<pair<int, int>> ans;
int main()
{
fast;
int T;
cin >> T;
while (T--)
{
ans.clear();
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> A[i];
f1[A[i]] = i;
}
for (int i = 1; i <= n; i++)
{
cin >> B[i];
f2[B[i]] = i;
}
int flag = 1;
for (int i = n; i > 1; i--)
{
int L = f1[i], R = f2[i];
if (L > R)
{
flag = 0;
break;
}
if (L == R)
{
continue;
}
int l = 1, r = 0;
for (int j = L + 1; j <= R; j++)
{
if (A[j] > i)
continue;
while (l <= r && A[j] > m[r])
r--;
m[++r] = A[j];
}
if (l > r)
{
flag = 0;
break;
}
while (L < R)
{
ans.pb(make_pair(L, f1[m[l]]));
// cout<<L<<' '<<f1[m[l]]<<'
';
swap(A[L], A[f1[m[l]]]);
swap(f1[i], f1[m[l]]);
L = f1[i];
l++;
// for (int j = 1; j <= n; j++)
// {
// cout << A[j] << ' ';
// }
// cout << '
';
// for (int j = 1; j <= n; j++)
// {
// cout << f1[j] << ' ';
// }
// cout << '
';
// for (int j = l; j <= r; j++)
// {
// cout << m[j] << ' ';
// }
// cout << '
';
}
if (flag == 0)
break;
}
if (flag)
{
cout << ans.size() << '
';
for (int i = 0; i < ans.size(); i++)
{
cout << ans[i].first << ' ' << ans[i].second << '
';
}
}
else
cout << "-1
";
}
}
G - Occupy the Cities
到死都没能想到很好的方法,看了题解是动态规划就像头被雷劈了。(学了这么久dp看不出这是个dp,说正常也正常,不正常也不正常)
赛时想的都是什么牛鬼蛇神。
什么(010)如果第一个(1)向左可以等价为(01100)然后七搞八搞搞不清楚最后写也写不完。
E - Buy and Delete
对于一个有向无环图,(Bob)可以一轮删完。对于一个有环图,无论如何(Bob)都可以两步删完(一步拆环一步拆剩余)。最终答案必定在(0,1,2)之中。
- 如果没有办法购买任意一条边,答案为(0)。
- 如果能够购买一个有向环,答案为(2)。
- 否则,答案为(1)。
对于1,判一轮就行;对于2和3,只需要找到最小有向环权值是否小于题目中给出的(c)。
所以只需要找最小有向环即可。
然后,只会(dfs),T了,直接判了死刑。
对(n)个点都进行(dij),枚举点对求连通性和最小值。
补了一发,不知道能不能过,等上gym了再说。
#include <bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 2e3 + 10; // 这里做了修改,当时没注意范围
struct qnode
{
int v;
int c;
qnode(int _v = 0, int _c = 0) : v(_v), c(_c) {}
bool operator<(const qnode &r) const
{
return c > r.c;
}
};
struct Edge
{
int v;
int cost;
Edge(int _v = 0, int _cost = 0) : v(_v), cost(_cost) {}
};
vector<Edge> E[maxn];
bool vis[maxn];
int dis[maxn][maxn];
void dijkstra(int n, int start)
{
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i++)
{
dis[start][i] = INF;
}
priority_queue<qnode> q;
while (!q.empty())
q.pop();
dis[start][start] = 0;
q.push(qnode(start, 0));
qnode tmp;
while (!q.empty())
{
tmp = q.top();
q.pop();
int u = tmp.v;
if (vis[u])
continue;
vis[u] = 1;
for (int i = 0; i < E[u].size(); i++)
{
int v = E[tmp.v][i].v;
int cost = E[tmp.v][i].cost;
if (!vis[v] && dis[start][v] > dis[start][u] + cost)
{
dis[start][v] = dis[start][u] + cost;
q.push(qnode(v, dis[start][v]));
}
}
}
}
void addedge(int u, int v, int w)
{
E[u].pb(Edge(v, w));
}
int main()
{
fast;
int n, m, c;
int u, v, w;
cin >> n >> m >> c;
int flag = 0;
for (int i = 1; i <= m; i++)
{
cin >> u >> v >> w;
if (w <= c)
flag = 1;
addedge(u, v, w);
}
if (!flag)
{
cout << "0
";
return 0;
}
for (int i = 1; i <= n; i++)
{
dijkstra(n, i);
}
int ans = INF;
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j++)
{
if (dis[i][j] != INF && dis[j][i] != INF)
{
ans = min(dis[i][j] + dis[j][i], ans);
}
}
}
if (ans <= c)
cout << "2
";
else
cout << "1
";
}
铁牌,CCPC的第一块铁牌,大概率也是最后一块,下周ICPC济南700+冲210,只能说自求多福了。
大不了再打一年.jpg