問題レポートの解決「のCSP学校の訓練2019年10月30日」
T1、木
トピックリンク(エスケープ)
\(説明\) :
今木、の合計がある\(N \)のノードが。
ルートノード:これは状態\(1 \)ノードの数を、各ノードがポイントに権利を有します。
今、\(M \)操作はツリーを完了するために必要な、各操作を3のいずれかである:
\(1 \ X \ A \) :動作\(1 \) 、ノード\(X \)ポイントを増やす右\(A \) 。
\(2 \ X \ A \) :動作\(2 \) 、ノードがあろう(\ X \)増加ルートとするサブツリー内の全ての点の重みである\(A \) 。
\(3 \ X \) :動作\(3 \) 、クエリノード\(X \)は、すべての点および経路の重みのルートノードを指します。
以下のための\(100 \%\)のデータ:\(N、M \ 5のLeq 10 ^ \。) 。
すべての入力データは、絶対値超えないことを確実にするために\(6 10 ^ \) 。
どうやらあなたは、ツリーを横断することができます。時間複雑度\(O(Mする\ n-log_2)\) 。
\(溶液\) :
あなたは、すべてのノード上で動作することができる\(DFS \) ;トラバーサル、修正バックトラックの後に完全なサブツリーを横断する際に横断し
、シングルポイントがサブツリー全体プラス解答に変更\(\) 、またはサブツリー\を( U \)ポイントで\(V \)答えは増加する- (dep_u - \(dep_v \タイムズAを1)\タイムズA \) 。
時間複雑度\(O(Mする\ n-log_2)\) 。
\(出典\) :
#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
const int N = 1e5 + 5;
struct edge {
int next, to;
} e[N << 1];
int ecnt = 1, head[N];
int n, m, w[N];
inline void add_edge(const int u, const int v) {
e[++ecnt] = (edge){head[u], v}, head[u] = ecnt;
e[++ecnt] = (edge){head[v], u}, head[v] = ecnt;
}
struct segment_tree {
long long sum[N << 2];
long long lazy[N << 2];
inline void push_up(const int p) {
sum[p] = sum[p << 1] + sum[p << 1 | 1];
}
inline void spread(const int p, const int tl, const int tr, const int mid) {
sum[p << 1] += lazy[p] * (mid - tl + 1);
sum[p << 1 | 1] += lazy[p] * (tr - mid);
lazy[p << 1] += lazy[p];
lazy[p << 1 | 1] += lazy[p];
lazy[p] = 0;
}
void modify(int l, int r, int k, int tl = 1, int tr = n, int p = 1) {
if (l <= tl && tr <= r) {
sum[p] += 1ll * k * (tr - tl + 1);
lazy[p] += k;
return ;
}
int mid = (tl + tr) >> 1;
if (lazy[p])
spread(p, tl, tr, mid);
if (mid >= l)
modify(l, r, k, tl, mid, p << 1);
if (mid < r)
modify(l, r, k, mid + 1, tr, p << 1 | 1);
push_up(p);
}
long long query(int l, int r, int tl = 1, int tr = n, int p = 1) {
if (l <= tl && tr <= r)
return sum[p];
int mid = (tl + tr) >> 1;
if (lazy[p])
spread(p, tl, tr, mid);
if (mid < l)
return query(l, r, mid + 1, tr, p << 1 | 1);
if (mid >= r)
return query(l, r, tl, mid, p << 1);
return query(l, r, tl, mid, p << 1) + query(l, r, mid + 1, tr, p << 1 | 1);
}
} T;
//heavy-light decomposition begin
int siz[N], hson[N], fa[N], fro[N], dfn[N];
void dfs1(const int u) {
siz[u] = 1;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if (v == fa[u])
continue;
fa[v] = u;
dfs1(v);
siz[u] += siz[v];
if (siz[v] > siz[hson[u]])
hson[u] = v;
}
}
void dfs2(const int u, const int tp) {
fro[u] = tp;
dfn[u] = ++dfn[0];
if (hson[u])
dfs2(hson[u], tp);
for (int i = head[u]; i; i = e[i].next)
if (e[i].to != fa[u] && e[i].to != hson[u])
dfs2(e[i].to, e[i].to);
}
//heavy-light decomposition end
long long query(int u) {
long long ret = 0;
while (u) {
ret += T.query(dfn[fro[u]], dfn[u]);
u = fa[fro[u]];
}
return ret;
}
int main() {
//freopen("in", "r", stdin);
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
n = in(), m = in();
for (int i = 1; i <= n; ++i)
w[i] = in();
for (int i = 1; i < n; ++i)
add_edge(in(), in());
dfs1(1), dfs2(1, 1);
for (int i = 1; i <= n; ++i)
T.modify(dfn[i], dfn[i], w[i]);
while (m--) {
int typ = in(), x = in(), a;
if (typ == 1) {
a = in();
T.modify(dfn[x], dfn[x], a);
} else if (typ == 2) {
a = in();
T.modify(dfn[x], dfn[x] + siz[x] - 1, a);
} else {
printf("%lld\n", query(x));
}
}
return 0;
}
T2、図。
トピックリンク(エスケープ)
\(説明\) :
無向グラフ:CO \(\ N-)ノード、それぞれの番号(1 \)\〜\(\ N-) 、一方\(m個\)条無向エッジ。
木とは違って、彼は勉強して、グラフの点におけるエッジは自重、持っている\(私は\)右の側縁\(W_i \) 、\(私は\)右の点にポイントを\ (C_I \) 。
点から\(S \)の辺の数を通る点に\(T \)コストは次のように定義される:右の両側縁間の経過と、を含む、全ての点(添加した後\(S \)をし、\ (T \)最大点右)。
Makik今与えられ\(K \)回の問い合わせ、各所定の二つの整数\(S、T \)問い合わせから、\(S \)に(T \)\最小コスト。
Makikの設計アルゴリズムはすぐに答えを解決するために助けてください。
注:2つのポイント間の図複数のエッジの場合に存在することができるが、ループが存在しないからです。
\(1 \当量のn \の当量250 1 \当量のM <= 10000、1 \当量のK \当量10000 \)、\は、
(1 \は、B、S、1当量 T \当量のN、 wは、1つの\当量cを\当量100000、S \ NEトン\) 。
\(溶液\) :
唯一の点が追加されている取ることができ、出発点、単一始点最短ランを列挙大に小さいから点右をクリックして、マップを追加し、
その後、中間点として点\(X- \) 、更新\(F_ { U、V} \)(\(U、V \)との間の回答):
\(F_ {U、V} = maxの\ {dis_ {U、X \} + {X DIS、V + c_x} \} \)ここで、\((U、X-)\)、\ ((X-は、V)\)現在の数字に到達しなければなりません。
時間複雑\(O(N ^ 3)\) 。
\(出典\) :時間計算
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
const int N = 255, M = 1e4 + 5;
int n, m, k;
int c[N], id[N];
int dis[N][N], e[N][N], f[N][N];
bool ok[N];
inline bool cmp(const int &i, const int &j) {
return c[i] < c[j];
}
void dijkstra(const int s, int *d) {
static int vis[N];
d[s] = 0;
for (int i = 1, u; i < n; ++i) {
u = 0;
for (int v = 1; v <= n; ++v)
if (ok[v] && vis[v] != s && ~d[v] && (!~d[u] || d[u] > d[v]))
u = v;
if (!u)
break;
vis[u] = s;
for (int v = 1; v <= n; ++v)
if (ok[v] && ~e[u][v]) {
if (!~d[v])
d[v] = d[u] + e[u][v];
else
chk_min(d[v], d[u] + e[u][v]);
}
}
}
inline void init() {
memset(dis, -1, sizeof(dis));
memset(f, -1, sizeof(f));
memset(e, -1, sizeof(e));
}
int main() {
//freopen("in", "r", stdin);
freopen("roadtoll.in", "r", stdin);
freopen("roadtoll.out", "w", stdout);
init();
n = in(), m = in(), k = in();
for (int i = 1; i <= n; ++i)
c[i] = in(), id[i] = i;
for (int i = 1, u, v, w; i <= m; ++i) {
u = in(), v = in(), w = in();
if (!~e[u][v])
e[u][v] = w;
else
chk_min(e[u][v], w);
e[v][u] = e[u][v];
}
std::sort(id + 1, id + 1 + n, cmp);
for (int i = 1; i <= n; ++i) {
ok[id[i]] = 1, dijkstra(id[i], dis[id[i]]);
for (int j = 1; j <= n; ++j)
dis[j][id[i]] = dis[id[i]][j];
for (int u = 1; u <= n; ++u)
if (~dis[u][id[i]])
for (int v = u + 1; v <= n; ++v)
if (~dis[id[i]][v]) {
if (!~f[u][v])
f[u][v] = dis[u][id[i]] + dis[id[i]][v] + c[id[i]];
else
chk_min(f[u][v], dis[u][id[i]] + dis[id[i]][v] + c[id[i]]);
}
}
for (int u = 1; u <= n; ++u)
for (int v = u + 1; v <= n; ++v)
f[v][u] = f[u][v];
while (k--)
printf("%d\n", f[in()][in()]);
return 0;
}
T3、地図
トピックリンク(エスケープ)
\(説明\) :
詳細な市内地図、マークL景勝地とMakikは、番号を1〜L. リゾートと一方向の高速道路のリゾート間を内蔵。
その日、Makikは、この地域を訪問する、と彼は日帰り旅行を開始するために1景勝地を選択して、他の風光明媚な高速道路への一方向のみアクセスすることができます。
少なくとも2つの出発エリアに戻り、最後に再生し、景勝地を訪問します。
Makikが最初に訪問した場合は、\(私は\)景勝地を、楽しい値の取得\(F_iと\)を。そして素敵な景色の値は楽しいことではないでしょう参照してください。
最初のため\(Iは\)一方向高速チャネルの、消費\(T_I \)時間であることができる\(L1_i \)に到達\(L2_i \) 。
時間を費やす必要はありませ景勝地を訪問し、問題を単純化するために、Makikは、単位時間あたりの最終値の最大の喜びを取得したいです。
あなたはMakikは、彼が得ることができる平均喜びの最大値を計算するのに役立つプログラムを書くことです。
\(2 \当量のL \の当量1000と、2 \当量Pの\の当量5000、1 \当量F_iと\の当量1000と、1 \当量T_I \当量\ 1000) 。
\(溶液\) :
明らかに\(01 \)スコアが計画し、その半分(イテレーションは書かないでいるようです)。
設けられた二つの分離\(MID \)を、次いで、(MID \)\:と環が存在する場合にのみ成立場合に可能
\ [\ FRAC {\ SUM F_iと} {\ SUMのe_i} \ GEQ MID \ IFF \ SUM F_iと> = MID \ GEQ e_i \ IFF \合計のf_i -
\合計中旬\ CDOT e_i \ GEQ 0 \] 仲裁リングは、正することができます。
時間複雑度\(O(G(N、M)\ log_2 ANS)\) 、ここで\(G(N、M) \) 複雑環を放棄する、と考えることができる\(O(NM)\) 。
\(出典\) :
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
typedef double db;
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
const int N = 1e3 + 5;
const double eps = 1e-4;
struct edge {
int next, to;
double w;
} e[N * 5];
int ecnt = 1, head[N];
int f[N];
int n, m;
db d[N];
bool vis[N];
bool dfs(int u, db mid) {
if (vis[u])
return 1;
vis[u] = 1;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
db w = f[v] - mid * e[i].w;
if (d[v] <= d[u] + w) {
d[v] = d[u] + w;
if (dfs(v, mid))
return 1;
}
}
vis[u] = 0;
return 0;
}
bool chk(db mid) {
for (int i = 1; i <= n; ++i)
d[i] = vis[i] = 0;
for (int i = 1; i <= n; ++i)
if (dfs(i, mid))
return 1;
//std::queue<int> q;
//static int tim[N];
//for (int i = 1; i <= n; ++i)
// q.push(i), tim[i] = vis[i] = 1, d[i] = 0;
//while (!q.empty()) {
// int u = q.front(); q.pop();
// vis[u] = 0;
// if (tim[u] > n)
// return 1;
// for (int i = head[u]; i; i = e[i].next) {
// int v = e[i].to;
// db w = f[v] - mid * e[i].w;
// if (d[v] <= d[u] + w) {
// d[v] = d[u] + w;
// if (!vis[v])
// q.push(v), vis[v] = 1, ++tim[v];
// }
// }
//}
return 0;
}
db binary_search(db l, db r) {
while (r - l > eps) {
db mid = (l + r) / 2;
if (chk(mid))
l = mid;
else
r = mid;
}
return l;
}
int main() {
//freopen("in", "r", stdin);
freopen("travel.in", "r", stdin);
freopen("travel.out", "w", stdout);
n = in(), m = in();
for (int i = 1; i <= n; ++i)
f[i] = in();
while (m--) {
int u = in(), v = in(), w = in();
e[++ecnt] = (edge){head[u], v, 1.0 * w}, head[u] = ecnt;
}
printf("%.2lf\n", binary_search(0, 500));
return 0;
}