Dashboard - 2019-2020 ICPC Northwestern European Regional Programming Contest (NWERC 2019) - Codeforces をやった。
C,F,I は気づいたら通ってた
B,K はわからん
A
得点が同じ人ごとにまとめておいて、点数が変化した人の分だけ更新するようにシミュレーションすると通るらしい
D
まず の値に関しては答えに何も関係がない。あるパス の長さは なので結局定数を足したものと変わらない。以降では として考える。
とすると、辺の本数を最小化したパスが最短となることがわかる。ようするに を増やすと辺の本数が減少していくので、辺の本数と関連があることがわかる。
で辺の本数を に固定したとき、最短となるパスを求める。辺の本数頂点始点からの最短距離 とすると、 という遷移で計算できる。
辺の本数が 本のパスで のときの最短路長は である。 本の直線の中で最小になるような が存在する場合、辺の本数が のパスが最短になるような が存在する。このような直線集合はconvex hull trickの要領で求めることができる。
あとは長さが で最短となるようなパスに含まれる頂点を列挙し、答えから外す。残っている頂点集合を出力すれば良い。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using PII = pair<ll, ll>;
#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
template<typename T> void chmin(T &a, const T &b) { a = min(a, b); }
struct FastIO {FastIO() { cin.tie(0); ios::sync_with_stdio(0); }}fastiofastio;
const ll INF = 1LL<<60;
int main(void) {
ll n, m;
cin >> n >> m;
vector<vector<PII>> g(n);
REP(i, m) {
ll a, b, c;
cin >> a >> b >> c;
a--, b--;
g[a].push_back({b, c});
g[b].push_back({a, c});
}
vector<vector<ll>> dist(n, vector<ll>(n, INF));
dist[0][0] = 0;
REP(i, n-1) REP(j, n) for(auto to: g[j]) {
chmin(dist[i+1][to.first], dist[i][j] + to.second);
}
deque<PII> st;
auto check = [&](const PII &a, const PII &b, const PII &c) {
__int128_t xa = c.second-a.second, ya = a.first-c.first,
xb = b.second-a.second, yb = a.first-b.first;
return xa * yb < xb * ya;
};
auto insert = [&](ll a, ll b) {
PII p({a, b});
while(st.size()>=2 && check(st[st.size()-2], st[st.size()-1], p)) st.pop_back();
st.emplace_back(p);
};
for(int i=n-1; i>=1; --i) insert(i, dist[i][n-1]);
while(st.size() >= 2 && st[0].second > st[1].second) st.pop_front();
vector<bool> mark(n);
vector<vector<bool>> visit(n, vector<bool>(n));
auto dfs = [&](auto &&self, ll edge, ll v) -> void {
mark[v] = visit[edge][v] = true;
if(v == 0) return;
for(auto to: g[v]) {
if(dist[edge-1][to.first] + to.second == dist[edge][v]) {
if(visit[edge-1][to.first]) continue;
self(self, edge-1, to.first);
}
}
};
for(auto i: st) dfs(dfs, i.first, n-1);
vector<ll> ans;
REP(i, n) if(!mark[i]) ans.push_back(i);
cout << ans.size() << "\n";
REP(i, ans.size()) cout << ans[i]+1 << (i+1==(ll)ans.size() ? '\n' : ' ');
return 0;
}
E
時間 を試せばよい
小数を100倍して整数に直すときに、ちゃんと誤差を考慮してepsを足さないといけなくて、 とするべき
G
あるモンスター が出現 && 直前の出現するモンスターが であるときの寄与をそれぞれ足していく
H
区間 の斜度が 以上である
番目の高さから を引いておくと判定ができる。
答えは小数になることもあるので、各 について考えられるような最大の を整数で求めたあとどこまで伸ばせるか?を求めればよい
J
増減の操作を行う回数を に固定する。絶対値が 未満の数については自由に符号が変更できる。逆に絶対値が 以上の数については矛盾する部分は消去で対応する必要がある。 を固定したときの削除数を高速に求めることができれば解くことができる。
以上のみからなる数列で隣接していて、(距離が奇数 && 正負同じ) || (距離が偶数 && 正負違う) なら辺をつなぐとしたグラフを考える。このグラフで sum(連結成分の頂点数-1)=辺数 の分要素を消去する必要がある。
を大きくして頂点 が消える場合、今まで頂点 に隣接していた辺を消して、 の左右の頂点に辺をつなぐべきならばつなぐ。辺数を管理できるので解けた。