Codeforces Round #776 (Div. 3)
Codeforces Round #776 (Div. 3)
E
一个求划分段长度最小值最大化的问题。
题解写的贪心。
但我用二分写的
枚举最小距离,判断是否合法
合法性判断细节挺多的,肯定要移动距离最短的
在距离最短的两考试中,移动左边或者右边得分类(特判第一个)
同时放置位置在最后一门考试之后也得特判。
非常的恶心,代码也写的很丑
#include<vector>
#include<iostream>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int N,D;
cin >> N >> D;
vector<int> a(N + 2);
for(int i = 1;i <= N;i ++) {
cin >> a[i];
}
a[0] = 0;
a[N + 1] = D + 1;
sort(a.begin(),a.end());
auto chk = [&](int mid) {
int id = 0;
for(int i = 1;i <= N;i ++) {
if(a[i] - a[i - 1] - 1 < mid) { // 找不满足要求的
id = i;
break;
}
}
if(id == 0) return true;// 没有返回 true
vector<int> t;
for(int i = 0;i <= N + 1;i ++) {
if(i != id) t.push_back(a[i]);
}
bool ok = 0;
for(int i = 1;i < t.size();i ++) { // 移动右边的
if(((t[i] - t[i - 1] - 2 ) / 2) >= mid) {
ok = 1;
}
if(i + 1 == t.size()) {// 特判放最后
if((D - t[i - 1] - 1) >= mid) {
ok = 1;
}
}
if((t[i] - t[i - 1] - 1 ) < mid && i + 1 != t.size()) {
ok = 0;
break;
}
}
if(ok) {
return true;
}
if(id - 1 == 0) {// 特判最小段右边是第一个
return ok;
}
vector<int> tt;
for(int i = 0;i <= N + 1;i ++) {
if(i != id - 1) tt.push_back(a[i]);
}
for(int i = 1;i < tt.size();i ++) {
if((tt[i] - tt[i - 1] - 2) / 2 >= mid) {
ok = 1;
}
if(i + 1 == tt.size()) {
if((D - tt[i - 1] - 1) >= mid) {
ok = 1;
}
}
if((tt[i] - tt[i - 1] - 1) < mid && i + 1 != tt.size()) {
ok = 0;
break;
}
}
return ok;
};
int l = -1,r = D + 1;
while(l + 1 < r) {
int mid = l + r >> 1;
if(chk(mid)) l = mid;
else r = mid;
}
cout << l << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}
F
记录方案的01背包
但是还是代码实现起来有点恶心,还是coding能力不行
题中每任务都是独立的,所以对每一个任务按DDL顺序跑01背包就行
dp中记录的是完成任务的最短时间
跑完要记录方案
方法就是从后往前回溯,当dp值发生改变时,一定使用了当前决策
此时找出正确的状态转移,然后回溯下一次决策。
注意记录时间花费,判断是否超过DDL
代码参考jiangly,非常简洁优雅。
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
int N,M;
int ddl[MAXN];
struct Node {
int t,p,id;
};
void solve()
{
cin >> N >> M;
for(int i = 0;i < N;i ++) {
cin >> ddl[i];
}
vector<vector<Node>> a(N);
for(int i = 0;i < M;i ++) {
int t,p,e;
cin >> e >> t >> p;
a[-- e].push_back({t,p,i});
}
vector<int> ans;
int cur = 0;
for(int i = 0;i < N;i ++) {
int n = a[i].size();
vector<vector<int>> dp(n + 1,vector<int>(101,linf));
dp[0][0] = 0;
for(int j = 0;j < n;j ++) {
auto [t,p,id] = a[i][j];
dp[j + 1] = dp[j];
for(int k = 0;k <= 100;k ++) {
int nk = min(100ll,p + k);
dp[j + 1][nk] = min(dp[j + 1][nk],dp[j][k] + t);
}
}
cur += dp[n][100];
if(cur > ddl[i]) {
cout << -1 << endl;
return;
}
int ed = 100;
for(int j = n - 1;j >= 0;j --) {
auto [t,p,id] = a[i][j];
if(dp[j + 1][ed] == dp[j][ed]) {
continue;
}
ans.push_back(id);
for(int k = 0;k <= 100;k ++) {
if(min(100ll,k + p) == ed && dp[j + 1][ed] == dp[j][k] + t) {
ed = k;
break;
}
}
}
}
cout << ans.size() << endl;
for(int x : ans) {
cout << x + 1 << " ";
}
cout << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}
G
题目中虽然不保证路径是简单路径,但因为要算的是比最短路大1的次短路,图中又没有自环重边,如果跑环或者重复走一条路,长度最少加2,所以一定求的是简单路径。
然后这就是个次短路计数板子了
虽然是板子,但因为是第一次写,还有很多没想明白
本题因为是无权图,所以其实有更简单的BFS写法
可惜还不会(
迪杰斯特拉写法就是记录最短路和次短路的长度与数目
在跑迪杰斯特拉的过程中,如果满足
\[dis[v][0] > dis[u][flg] + w
\]
正常就会进行松弛操作,更新最短路
但是如果加入对次短路的考虑,可以发现这个还未更新的最短路在一次更新后就会变成新的次短路,并将之前的次短路给覆盖
因此在进行正常的最短路松弛之前,还要做次短路的记录
\[dis[v][1] = dis[v][0]
\]
\[cnt[v][1] = cnt[v][0]
\]
类似的
当发现有新的路径比当前的次短路还要小且比最短路大,这个新的次短路就要覆盖掉老的次短路
\[dis[v][1] = dis[u][flg] + w
\]
\[cont[v][1] = cnt[u][flg]
\]
当我们对次短路和最短路的状态进行更新后,要将这个结点入队。
最后答案就是最短路数量加上比比最短路长1的次短路数量
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
int N,M;
vector<vector<PII>> G;
struct Node {
int u,flg,d;
bool operator>(const Node& x) const {
return d > x.d;
}
};
int dij(int s,int f) {
vector< array<int,2> > cnt(N,{0,0}),dis(N,{linf,linf});
dis[s][0] = 0;
cnt[s][0] = 1;
priority_queue<Node,vector<Node> ,greater<Node> > q;
q.push({s,0,0});
vector< array<bool,2> > st(N,{0,0});
while(q.size()) {
auto [u,flg,d] = q.top();
q.pop();
if(st[u][flg]) continue;
st[u][flg] = 1;
for(auto [v,w] : G[u]) {
if(dis[v][0] > dis[u][flg] + w) {
dis[v][1] = dis[v][0];
cnt[v][1] = cnt[v][0];
q.push({v,1,dis[v][1]});
dis[v][0] = dis[u][flg] + w;
cnt[v][0] = cnt[u][flg];
q.push({v,0,dis[v][0]});
}else if(dis[v][0] == dis[u][flg] + w) {
cnt[v][0] += cnt[u][flg];
cnt[v][0] %= mod;
}else if(dis[v][1] > dis[u][flg] + w) {
dis[v][1] = dis[u][flg] + w;
cnt[v][1] = cnt[u][flg];
q.push({v,1,dis[v][1]});
}else if(dis[v][1] == dis[u][flg] + w) {
cnt[v][1] += cnt[u][flg];
cnt[v][1] %= mod;
}
}
}
int res = cnt[f][0];
if(dis[f][0] + 1 == dis[f][1]) (res += cnt[f][1]) %= mod;
return res;
}
void solve()
{
cin >> N >> M;
G.clear();
G.resize(N);
int s,f;
cin >> s >> f;
s -- ,f --;
for(int i = 0;i < M;i ++) {
int u,v;
int w = 1;
cin >> u >> v;
u --,v --;
G[u].push_back({v,w});
G[v].push_back({u,w});
}
cout << dij(s,f) << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}