[NOIP2015 提高组] 运输计划 - 洛谷
目录
测试点信息
P2680_1.in
P2680_1.out
图:
50分参考代码(开了n^2的数组,MLE了):
测试点信息
Subtask #0
#1
P2680_1.in
100 1
7 97 4
89 2 0
40 91 1
70 84 1
36 92 3
28 20 0
25 100 1
76 56 2
58 47 3
87 76 0
57 51 4
6 36 0
71 47 0
13 50 3
83 98 5
19 36 1
75 26 3
50 86 2
81 78 1
70 41 5
44 4 2
21 90 1
18 65 4
51 93 3
22 38 0
10 89 3
28 83 3
72 29 3
62 81 0
25 35 0
5 71 2
17 62 1
88 68 0
10 11 3
4 80 2
56 99 4
27 94 2
53 54 4
67 37 4
40 52 5
23 30 4
64 70 5
52 85 4
22 92 4
13 91 0
90 32 1
61 65 2
81 34 0
75 43 0
8 5 4
38 1 1
12 45 2
68 31 4
97 95 4
38 94 5
81 48 5
12 61 5
60 97 0
39 41 3
46 99 1
32 52 5
55 11 2
29 11 3
71 85 4
55 77 3
72 70 4
8 51 5
8 24 2
64 95 5
79 84 5
30 63 0
27 35 1
73 69 4
83 75 2
87 33 0
98 9 1
66 31 0
3 33 2
30 16 3
87 80 1
4 48 0
16 55 0
73 32 5
14 17 5
29 36 4
76 37 5
96 9 3
81 31 2
8 9 2
16 49 1
83 15 0
68 54 2
18 58 1
61 84 3
83 42 3
26 82 3
42 53 0
25 59 1
21 74 0
96 81
P2680_1.out
19
图:
我发现之间有5这个长度,弄这个是因为我差分写错了没发现。。
50分参考代码(开了n^2的数组,MLE了):
//所有飞船一起飞的啊,,,
//二分,分的是答案,超过的他们去减边,如果可以都比mid小,那么答案成立
struct edge
{
int u, v;
ll elen;
};
bool cmp(edge a,edge b)
{
return a.elen < b.elen;
}
void solve()
{
int n, m;
cin >> n >> m;
vector<vector<int>>alist(n + 1);
vector<vector<int>>cost(n + 1,vector<int>(n+1));
for (int i = 0; i < n - 1; i++)
{
int a, b, t;
cin >> a >> b >> t;
alist[a].push_back(b);
alist[b].push_back(a);
cost[a][b] = cost[b][a] = t;
}
vector<vector<int>>fa(n + 1, vector<int>(22));
vector<int>dep(n + 1);
vector<int>plen(n + 1);
auto dfs = [&](int cur, int pa, auto dfs)->void {
dep[cur] = dep[pa] + 1;
plen[cur] += cost[cur][pa];
for (auto x : alist[cur])
{
if (x != pa)
{
fa[x][0] = cur;
plen[x] = plen[cur];
dfs(x, cur, dfs);
}
}
};
dfs(1, 0, dfs);
//倍增求父亲
for (int p = 1; p < 22; p++)
{
for (int i = 1; i <= n; i++)
{
fa[i][p] = fa[fa[i][p - 1]][p - 1];
}
}
auto LCA = [&](int a, int b)->pair<int, int>
{
ll ans = 0;
if (dep[a] < dep[b])swap(a, b);
while (dep[a] > dep[b])
{
int dis = (int)log2(dep[a] - dep[b]);
a = fa[a][dis];
ans += pow(2, dis);
}
for (int i = log2(dep[a]); i >= 0; i--)
{
if (fa[a][i] != fa[b][i]) //向上结果不同才跳
{
a = fa[a][i];
b = fa[b][i];
ans += pow(2, i) * 2;
}
}
if (a != b)
{
a = b = fa[a][0];
ans += 2;
}
return { a ,ans };
};
//两条边的距离就是到根节点,然后减去lca到根节点。
vector<edge>channel(m);
//vector<int>u(m),v(m);
//vector<ll>elen(m);//所有边长数组
for (int i = 0; i < m; i++)
{
cin >> channel[i].u >> channel[i].v;
int u = channel[i].u, v = channel[i].v;
channel[i].elen = (ll)plen[u] + plen[v] - (ll)plen[LCA(u, v).first]*2;
}
sort(channel.begin(), channel.end(),cmp);
auto check = [&](int mid)->bool
{
int l = 0, r = m-1;
while (l < r)
{
int m = l + (r - l) / 2;
if (channel[m].elen > mid)r = m;
else l = m+1;
}
// l ~ m-1 差分
vector<int>arr(n + 1);
for (int i = l; i < m; i++)
{
arr[channel[i].u]++;
arr[channel[i].v]++;
int f = LCA(channel[i].u, channel[i].v).first;
arr[f] -= 2;
}
priority_queue<int, vector<int>, less<int>>aedge;
auto dfs2 = [&](int cur, int pa, auto dfs2)->int
{
ll cn = arr[cur];
for (auto x : alist[cur])
{
if (x != pa)
{
cn += dfs2(x, cur, dfs2);
}
}
if (cn == m - l)
aedge.push(cost[cur][pa]);
return cn;//哇,差分啊当然返回总的啊
};
dfs2(1, 0, dfs2);
//
if (aedge.size() == 0)
return false;
if (channel[m - 1].elen - aedge.top() <= mid)
return true;
return false;
};
ll left = 0, right = channel[m - 1].elen;
while (left<right)
{
ll mid = left + (right - left) / 2;
if (check(mid))right = mid;
else left = mid + 1;
}
cout << left;
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
95分邻接表被卡常:
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch>'9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
inline void write(int x)
{
if (x < 0)
{
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
struct edge
{
int u, v;
ll elen;
};
bool cmp(edge a,edge b)
{
return a.elen < b.elen;
}
struct node
{
int b, t;
};
void solve()
{
int n, m;
cin >> n >> m;
vector<vector<node>>alist(n + 1);
for (int i = 0; i < n - 1; i++)
{
int a, b, t;
//cin >> a >> b >> t;
a = read();
b = read();
t = read();
alist[a].push_back({ b,t });
alist[b].push_back({ a,t });
}
vector<vector<int>>fa(n + 1, vector<int>(22));
vector<int>dep(n + 1);
vector<int>plen(n + 1);
auto dfs = [&](int cur, int pa, auto dfs)->void {
dep[cur] = dep[pa] + 1;
for (auto y : alist[cur])
{
int x = y.b;
if (x != pa)
{
fa[x][0] = cur;
plen[x] += y.t+plen[cur];
dfs(x, cur, dfs);
}
}
};
dfs(1, 0, dfs);
//倍增求父亲
for (int p = 1; p < 22; p++)
{
for (int i = 1; i <= n; i++)
{
fa[i][p] = fa[fa[i][p - 1]][p - 1];
}
}
auto LCA = [&](int a, int b)->pair<int, int>
{
ll ans = 0;
if (dep[a] < dep[b])swap(a, b);
while (dep[a] > dep[b])
{
int dis = (int)log2(dep[a] - dep[b]);
a = fa[a][dis];
ans += pow(2, dis);
}
for (int i = log2(dep[a]); i >= 0; i--)
{
if (fa[a][i] != fa[b][i]) //向上结果不同才跳
{
a = fa[a][i];
b = fa[b][i];
ans += pow(2, i) * 2;
}
}
if (a != b)
{
a = b = fa[a][0];
ans += 2;
}
return { a ,ans };
};
vector<edge>channel(m);
for (int i = 0; i < m; i++)
{
//cin >> channel[i].u >> channel[i].v;
channel[i].u = read();
channel[i].v = read();
int u = channel[i].u, v = channel[i].v;
channel[i].elen = (ll)plen[u] + plen[v] - (ll)plen[LCA(u, v).first]*2;
}
sort(channel.begin(), channel.end(),cmp);
auto check = [&](int mid)->bool
{
int l = 0, r = m-1;
while (l < r)
{
int m = l + (r - l) / 2;
if (channel[m].elen > mid)r = m;
else l = m+1;
}
// l ~ m-1 差分
vector<int>arr(n + 1);
for (int i = l; i < m; i++)
{
arr[channel[i].u]++;
arr[channel[i].v]++;
int f = LCA(channel[i].u, channel[i].v).first;
arr[f] -= 2;
}
//priority_queue<int, vector<int>, less<int>>aedge;
ll maxo = -1;
auto dfs2 = [&](int cur, int pa, auto dfs2)->int
{
ll cn = arr[cur];
for (auto y : alist[cur])
{
int x = y.b;
if (x != pa)
{
cn += dfs2(x, cur, dfs2);
}
}
if (cn == m - l)
{
maxo = max(maxo, (ll)plen[cur] - plen[pa]);
}
return cn;//哇,差分啊当然返回总的啊
};
dfs2(1, 0, dfs2);
//
if (maxo == -1)
return false;
if (channel[m - 1].elen - maxo <= mid)
return true;
return false;
};
ll left = 0, right = channel[m - 1].elen;
while (left<right)
{
ll mid = left + (right - left) / 2;
if (check(mid))right = mid;
else left = mid + 1;
}
//cout << left;
write(left);
}
signed main()
{
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
前面部分以及差分dfs改成链式前向星就过了:
数据:
登录 - Luogu Spilopelia
其实我看了下都是遍历,邻接表也一样。这样不用push_back节省了时间吧。
//#define int ll;
const ll inf = 1e9;
const ll mod = 998244353;
const ll maxn = 300005;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch>'9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
inline void write(int x)
{
if (x < 0)
{
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
int n, m;
struct node
{
int b, t, next;
}nodes[2 * maxn];
struct edge
{
int u, v;
ll elen;
};
bool cmp(edge a, edge b)
{
return a.elen < b.elen;
}
int k = 0;
int head[maxn];
inline void add(int a,int b,int t)
{
k++;
nodes[k].b = b;
nodes[k].t = t;
nodes[k].next = head[a];
head[a] = k;
}
int vis[maxn];
int fa[maxn][22];
int dep[maxn];
int plen[maxn];
void dfs(int cur, int pa)
{
dep[cur] = dep[pa] + 1;
vis[cur] = 1;
for (int i=head[cur];i>0;i= nodes[i].next)
{
int x = nodes[i].b;
if (vis[x])continue;
fa[x][0] = cur;
plen[x] += nodes[i].t + plen[cur];
dfs(x,cur);
}
};
void solve()
{
cin >> n >> m;
for (int i = 0; i < n - 1; i++)
{
int a, b, t;
a = read();b = read();t = read();
add(a, b, t);
add(b, a, t);
}
dfs(1, 0);
//倍增求父亲
for (int p = 1; p < 22; p++)
{
for (int i = 1; i <= n; i++)
{
fa[i][p] = fa[fa[i][p - 1]][p - 1];
}
}
auto LCA = [&](int a, int b)->pair<int, int>
{
ll ans = 0;
if (dep[a] < dep[b])swap(a, b);
while (dep[a] > dep[b])
{
int dis = (int)log2(dep[a] - dep[b]);
a = fa[a][dis];
ans += pow(2, dis);
}
for (int i = log2(dep[a]); i >= 0; i--)
{
if (fa[a][i] != fa[b][i]) //向上结果不同才跳
{
a = fa[a][i];
b = fa[b][i];
ans += pow(2, i) * 2;
}
}
if (a != b)
{
a = b = fa[a][0];
ans += 2;
}
return { a ,ans };
};
vector<edge>channel(m);
for (int i = 0; i < m; i++)
{
//cin >> channel[i].u >> channel[i].v;
channel[i].u = read();
channel[i].v = read();
int u = channel[i].u, v = channel[i].v;
channel[i].elen = (ll)plen[u] + plen[v] - (ll)plen[LCA(u, v).first]*2;
}
sort(channel.begin(), channel.end(),cmp);
auto check = [&](int mid)->bool
{
int l = 0, r = m-1;
while (l < r)
{
int m = l + (r - l) / 2;
if (channel[m].elen > mid)r = m;
else l = m+1;
}
// l ~ m-1 差分
vector<int>arr(n + 1);
for (int i = l; i < m; i++)
{
arr[channel[i].u]++;
arr[channel[i].v]++;
int f = LCA(channel[i].u, channel[i].v).first;
arr[f] -= 2;
}
//priority_queue<int, vector<int>, less<int>>aedge;
ll maxo = -1;
auto dfs2 = [&](int cur, int pa, auto dfs2)->int
{
ll cn = arr[cur];
vis[cur] = 1;
for (int i = head[cur]; i > 0; i = nodes[i].next)
{
int x = nodes[i].b;
if (vis[x])continue;
cn += dfs2(x, cur, dfs2);
}
if (cn == m - l)
{
maxo = max(maxo, (ll)plen[cur] - plen[pa]);
}
return cn;
};
memset(vis, 0, sizeof vis);
dfs2(1, 0, dfs2);
//
if (maxo == -1)
return false;
if (channel[m - 1].elen - maxo <= mid)
return true;
return false;
};
ll left = 0, right = channel[m - 1].elen;
while (left<right)
{
ll mid = left + (right - left) / 2;
if (check(mid))right = mid;
else left = mid + 1;
}
//cout << left;
write(left);
}
signed main()
{
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}