Codeforces Round #776 (Div
Codeforces Round #776 (Div. 3)
CodeForces - 1650D Twist the Permutation
给定你数组a:1 2 3 ... n,一共有n次操作,每次操作可以把\(a_i\)移到最左边,然后对\(i+1\)位以后不会产生影响,每次操作可以进行任意次,例如 :5 4 3 1 2 ,现在处于第三次操作,那么我只能在\(a_3\)位置上操作,操作一次后:
3 5 4 1 2,操作两次后:4 3 5 1 2,以此类推,现在给你操作完之后的数组a,让你计算出他在每个位置上的操作数分别是多少?
题解:思维+模拟
我们可以倒过来想,因为每一次操作不会对他后面的数造成影响,所以元素n现在的位置一定是最后一次操作操作若干次得到的,那么我们就得到了最后一次的操作数,根据这个操作数我们进行还原,还原得到的数组中n-1的位置一定是倒数第二次操作得到的,这样我们就得到了倒数第二次的操作数....以此类推
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;
int n, a[N], b[N];
int ans[N];
void solve()
{
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
for (int i = n; i >= 1; --i)
{
int step = -1;
if (i != n)
{
for (int j = 1; j <= n; ++j)
a[j] = b[j];
}
for (int j = 1; j <= i; ++j)
if (a[j] == i)
{
if (j != i)
step = j;
else
step = 0;
break;
}
if (step == -1)
{
cout << -1 << endl;
return;
}
ans[i] = step;
for (int j = 1; j <= i; ++j)
{
int pos = (j - step + i) % i;
if (pos == 0)
pos += i;
b[pos] = a[j];
}
b[i] = i;
}
for (int i = 1; i <= n; ++i)
cout << ans[i] << " ";
cout << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
CodeForces - 1650E Rescheduling the Exam
给你一张1-d长度的时间表,在这个时间表内安排了n场考试,你现在只能任意改变一场考试的时间,使得两场考试之间的休息时间u的最小值最大
题解:贪心+模拟
其实二分答案也能做,但是太难调了,直接放弃了,我们发现如果要使u变大,我们需要改变休息时间最短的两场考试\(a_i,a_{i-1}\)其中之一,那么假设我们改变\(a_i\)的时间,那么它可以插入的地方只有两个地方才有可能使得答案变大:
- 休息时间最长的两场考试中间,那么插入后他们之间的休息时间为:\((a_j-a_{j-1}-2)/2\)
- 插在最后一天\(d\)处,休息时间为:\(d-a[last]-1\),注意\(a[last]\)是会改变的
那么我们贪心在这两种插入中我们选择最大值,然后最大值再和改变后休息时间最短\(minn\)取\(min\)即可
那么我们已经考虑了改变\(a_i\)的情况,对于\(a_{i-1}\)只要\(i>1\)我们都可以按照上面的流程再走一遍,看一下u会不会更大
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;
int n, d;
int a[N];
int cal(int pos)
{
int pre = 0;
int minn = INF, maxx = -INF;
for (int i = 1; i <= n; ++i)
{
if (i != pos)
{
minn = min(minn, a[i] - a[pre] - 1);
maxx = max(maxx, a[i] - a[pre] - 1);
pre = i;
}
}
return min(minn, max((maxx - 1) / 2, d - a[pre] - 1));
}
void solve()
{
cin >> n >> d;
for (int i = 1; i <= n; ++i)
cin >> a[i];
int minn = INF;
int pos = -1;
for (int i = 1; i <= n; ++i)
if (a[i] - a[i - 1] - 1 < minn)
{
pos = i;
minn = a[i] - a[i - 1] - 1;
}
int ans = -INF;
ans = max(ans, cal(pos));
if (pos > 1)
ans = max(ans, cal(pos - 1));
cout << ans << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
CodeForces - 1650F Vitaly and Advanced Useless Algorithms
现在给你n个任务,每个任务都有截止时间,你需要将每个任务进程都完成100%,才算成功,又给定m种操作,每次操作给定\(e,t,p\),代表对于任务e,可以利用t时间将其进度增加p,每种操作只能用一次,求最后能否完成所有任务,如果能完成求出完成所有任务的最短用时,并将选择的操作以任意顺序输出
题解:01背包求方案数,感觉是一道好题目,下次记得复习
首先这是一个先明显的01背包求方案数的题目,因为存在截止时间,所以我们肯定要从截止时间短的任务开始做起,我们只需要对于每一个任务所对应的操作进行01背包,然后如果该任务完成的最短时间超过其给定的时间,那么说明后面的任务都完成不了了,如果能完成,那么剩余的时间我们可以分配给下一个任务,那么对于每次任务我们在dp时再记录方案即可
对于这道题,两个重点我们需要把握:
- 如何在一维滚动数组中记录方案数,我们可以开个vector[N],对于每次转移我们先将方案数转移,然后再在vector[]后面加上本次操作的id,\(pre[j] = pre[max(0ll, j - p)],\ \ \ \ pre[j].push\_back(id);\)
- 如何对所有操作数按照任务分类,同样可以利用vecrot,借助邻接表的思想进行分类
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 1e5 + 10, M = 4e5 + 10;
int n, m;
vector<array<int, 3>> v[N];
int a[N], f[N];
int dif[N];
vector<int> ans;
void solve()
{
ans.clear();
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> a[i];
for (int i = 1; i <= n; ++i)
dif[i] = a[i] - a[i - 1];
for (int i = 1; i <= n; ++i)
v[i].clear();
for (int i = 1, e, t, p; i <= m; ++i)
{
cin >> e >> t >> p;
v[e].push_back({t, p, i});
}
for (int i = 1; i <= n; ++i)
{
vector<int> pre[101];
f[0] = 0;
for (int j = 1; j <= 105; ++j)
f[j] = INF;
for (auto &[t, p, id] : v[i])
{
for (int j = 100; j >= 0; --j)
{
if (f[j] > f[max(0ll, j - p)] + t)
{
f[j] = f[max(0ll, j - p)] + t;
pre[j] = pre[max(0ll, j - p)];
pre[j].push_back(id);
}
}
}
if (f[100] > dif[i])
{
cout << -1 << endl;
return;
}
dif[i + 1] += dif[i] - f[100];
for (auto id : pre[100])
ans.push_back(id);
}
cout << ans.size() << endl;
for (auto id : ans)
cout << id << " ";
cout << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
CodeForces - 1650G Counting Shortcuts
给定一个无向图,没有自环和重边,给定起点st和终点ed,假设st到ed的最短距离为x,让你求出所有st到ed路径长度<=x+1的所有路径数(可以是简单路径,也可以不是简单路径)
题解:次短路和最短路求方案数 + 计数DP: 需要回顾复习
这是一道经典的最短路求方案数的问题,但同时我们只要再记录次短路的长度和方案数即可,我们可以在\(dijktra\)中对方案数进行记录和转移
现在解释一下\(dis[u][0/1]\):表示起点到u节点的最短距离(0)和次短距离(1)
同时我们在跑dij的时候将路径入队的同时需要分清楚这条路径是最短路还是次短路,我们可以定义结构体,在结构体中增加一个\(flag\)用来区分最短路和次短路
我们先来解释一下dp计数部分:
状态表示:\(f[u][0/1]\):代表从起点到u节点最短路径(0)的方案数,次短路(1)的方案数
状态属性:数量
状态转移:我们考虑四种情况
- \(dis[u][type]+w\)小于最短路\(dis[v][0]\),如果最短路已经被松弛过了,我们可以将其方案数和距离转移给次短距离,并将次短路入队,然后再将\(f[u][type]\)转移给最短路后入队;如果没有被松弛过,直接将\(f[u][type]\)转移给最短路后入队
- \(dis[u][type]+w\)等于最短路\(dis[v][0]\),该点最短路的方案数需要增加\(f[v][0] += f[u][type]\)
- \(dis[u][type]+w\)大于次短路\(dis[v][1]\),修改次短路后,对方案数进行转移\(f[v][1] = f[u][type]\),然后入队
- \(dis[u][type]+w\)等于次短路\(dis[v][1]\),该点次短路的方案需要增加\(f[v][1] += f[u][type]\)
那么对于答案输出部分:
如果\(f[ed][1]-f[ed][0]==1\),说明满足题目条件,答案为\(f[ed][0]+f[ed][1]\)
如果\(f[ed][1]-f[ed][0]!=1\), 不满足题目条件,答案为\(f[ed][0]\)
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;
int n, m;
vector<pii> g[N];
int st, ed;
int f[N][2], dis[N][2], vis[N][2];
struct node
{
int u, dis, flag;
bool operator<(const node &t) const
{
return dis > t.dis;
}
};
void dij(int st)
{
for (int i = 1; i <= n; ++i)
{
dis[i][0] = dis[i][1] = inf;
f[i][0] = f[i][1] = 0;
vis[i][0] = vis[i][1] = 0;
}
priority_queue<node> q;
q.push({st, 0, 0});
dis[st][0] = 0;
f[st][0] = 1;
while (q.size())
{
node t = q.top();
int u = t.u, type = t.flag;
q.pop();
if (vis[u][type])
continue;
vis[u][type] = 1;
for (auto &[v, w] : g[u])
{
if (dis[v][0] > dis[u][type] + w)
{
if (dis[v][0] != inf)
{
f[v][1] = f[v][0];
dis[v][1] = dis[v][0];
q.push({v, dis[v][1], 1});
}
f[v][0] = f[u][type];
dis[v][0] = dis[u][type] + w;
q.push({v, dis[v][0], 0});
}
else if (dis[v][0] == dis[u][type] + w)
f[v][0] = (f[v][0] + f[u][type]) % mod;
else if (dis[v][1] > dis[u][type] + w)
{
f[v][1] = f[u][type];
dis[v][1] = dis[u][type] + w;
q.push({v, dis[v][1], 1});
}
else if (dis[v][1] == dis[u][type] + w)
f[v][1] = (f[v][1] + f[u][type]) % mod;
}
}
}
void solve()
{
cin >> n >> m;
cin >> st >> ed;
for (int i = 1; i <= n; ++i)
g[i].clear();
for (int i = 1, u, v; i <= m; ++i)
{
cin >> u >> v;
g[u].push_back({v, 1});
g[v].push_back({u, 1});
}
dij(st);
if (dis[ed][1] - dis[ed][0] == 1)
cout << (f[ed][0] + f[ed][1]) % mod << endl;
else
cout << f[ed][0] << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}