- 题意:给一个(n)个点的(DAG),求在最长路上,删除一条边后,从某个入点到达出点的最长路最短是多少。
- 题解:首先可以先求出每个点到达出点的最长距离和次长距离,然后枚举最长路,在最长路上删边。因为起点不固定,所以建一个超级源点,然后从这个超级源点连向每个点一条边。这就变成了,从起点到出点最长路上删一条边使得删后的最长路最小,求删除后的最长路。可以设一个一维数组(Maxlen),(Maxlen_{i})表示了从(i)到出点的最长路,可以通过每次向下(dfs)返回值然后取(max)就行。并且当(dfs2)模拟沿着最长路走的时候,就只需(Maxlen[u] = Maxlen[v] + 1)看看是否成立,如果成立,那么(v)是(u)的下一个的最长路,但是会有多个,如果是个最长路的分叉,那么这里对答案不做贡献,无影响,但是不知道这条分叉的最长路走下去啥情况,不仅要走原来的路,还要走这条最长路的分岔口继续走下去,不过这个分岔口不影响答案。用(ans)数组,记录当前答案,设(dfs2)沿着最长路中走过的距离为(len),那么记录的答案就是之前沿着走过的,加上当前点的除最长路以外的最长的路径长度(Maxlen2)的长度,意味着,当前所记录的结果是,沿着最长路,从起点走到这里的路径长度,加上,从这里走的除了最长路以外的最长的路径长度,我们记录完成,然后顺着(ans)数组取(min)即可.注意,分岔口不能算,(ans)初始是(Maxlen_{0}),即应该是有多条长度一样并且不交叉的最长路,切记初始的时候图是有个超级源点,最后求的是从起点(0)到终点的答案长度,所以我们要求的应该(ans-1).
- 代码:
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 99;
vector<int>G[N];
int vis[N];
int Max_len[N];
int Max_len2[N];
int cnt[N];
int ans[N];
int dfs(int u) {
if (vis[u] == 1) {
return Max_len[u];
}
vis[u] = 1;
int tmp = 0;
for (auto v : G[u]) {
tmp = dfs(v) + 1;
if (tmp > Max_len[u]) {
Max_len2[u] = Max_len[u];
Max_len[u] = tmp;
} else if (tmp > Max_len2[u]){
Max_len2[u] = tmp;
}
}
return Max_len[u];
}
void dfs2(int u, int len) {
if (vis[u] == 2) return;
vis[u] = 2;
for (auto v : G[u])
{
if (Max_len[u] == Max_len[v] + 1) {
dfs2(v, len + 1);
cnt[len]++;
ans[len] = Max_len2[u] + len;
}
}
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)G[0].push_back(i);
for (int i = 1, u, v; i <= m; i++) {
cin >> u >> v;
G[u].push_back(v);
}
dfs(0);
dfs2(0, 0);
int Ans = Max_len[0] ;
for (int i = 1; i <= n; i++)if (cnt[i] == 1 ) {
Ans = min(Ans, ans[i]);
}
cout << Ans - 1 << endl;
}
- 题意: 给(n)个人,(k)辆车,一辆车最多坐(4)人,然后每个人去目的地花费一定的时间,要求出最少花费的时间。
- 题解: 一开始乱想,其实发现过程中无法表示,所以应该是二分时间,(check)函数就是,在时间里让前(k)快的司机一直拉人,最后计算一下是否可以把所有人都拉走。
- 代码:
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll N = 200009;
ll a[N];
ll n, k;
bool check(ll mid) {
ll cnt = 0;
for (ll i = 1; i <= min(k, n); i++) {
ll now = mid;
if (mid >= a[i]) {
cnt += 5;
now -= a[i];
} else break;
ll cost = 2 * a[i];
cnt += now / cost * 4;
if (cnt >= n)return 1;
}
return 0;
}
signed main() {
cin >> n >> k;
ll l = 0, r = 0x7fffffffffffffff;
for (ll i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + 1 + n);
while (l < r) {
ll mid = (l + r) >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
cout << l << endl;
}
- 题意:给出(n)个点, (m)条无向边, (1 <= m,n<= 200 000),然后给出(7)个位置,求从(1)出发依次全部经过这(7)个位置的最短路径。
- 题解:dij感觉都能想到,并且不能从(1)点贪心的去走,这个也好想,并且还得知道要得有(O(A^{7}_{7}))的常数,dij的复杂度是(O(nlogn)),所以乘起来不大行,想到用数组分别记录从这7个点开始的dij的(dis)数组,并且排列数用next_pumutation很好用。
- 代码:
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll N = 200009;
const ll inf = 0x3f3f3f3f3f3f3f3f;
struct edge {
ll v, w;
};
vector<edge>G[N];
struct Node {
ll pos,dis;
bool operator<(Node rhs)const {
return rhs.dis < dis;
}
};
ll n, m;
priority_queue<Node>pq;
ll d[10][N];
bool vis[N];
void dij(ll s, ll rk) {
while (!pq.empty()) pq.pop();
for (int i = 1; i <= n; i++) {
d[rk][i] = inf;
}
memset(vis, 0, sizeof vis);
d[rk][s] = 0;
pq.push({s, d[rk][s]});
while (!pq.empty()) {
auto now = pq.top();
pq.pop();
ll u = now.pos;
if (vis[u])continue;
vis[u] = 1;
for (auto t : G[u]) {
if (d[rk][t.v] > d[rk][u] + t.w) {
d[rk][t.v] = d[rk][u] + t.w;
pq.push({t.v, d[rk][t.v]});
}
}
}
}
ll a[9]{};
ll ans = 0x7fffffffffffffff;
void dfs(ll len,ll now, ll pre) {
if (len == 7) {
ans = min(ans, now);
return;
}
for (ll i = 1; i <= 7; i++) {
if (vis[i] == 1) {
continue;
}
vis[i] = 1;
dfs(len + 1, now + d[pre][a[i]], i);
vis[i] = 0;
}
}
signed main() {
ios::sync_with_stdio(0);
cin >> n >> m;
for (ll i = 1; i <= m; i++) {
ll u, v, w;
cin >> u >> v >> w;
G[u].push_back({v, w});
G[v].push_back({u, w});
}
a[0] = 1;
for (ll i = 1; i <= 7; i++) cin >> a[i];
for (ll i = 0; i <= 7; i++) dij(a[i], i);
int b[] = {0, 1, 2, 3, 4, 5, 6, 7};
do {
ll now = 0;
for (int i = 1; i <= 7; i++) {
now += d[b[i-1]][a[b[i]]];
}
ans = min(ans, now);
} while (next_permutation(b + 1, b + 1 + 7));
cout << ans << endl;
}
- 题意:给(n < 2 imes 10^{5})的数量的数,数的大小范围在(int32)内,求一共有多少个连续序列,使得序列数字之和与数字之积相等。
- 题解:最暴力的办法就是(n ^ {2})暴力跑,但是复杂度在那里必然超时。于是分析得(1)很特殊,先设乘积为(prd), 和为(sum),(1)是唯一一种能延缓乘积增加,并且使(sum)增加的方法。还可发现,除了(1)就算是最小的(2),当连续的(2)用不了多长就会爆,更何况更大的数,所以如果是处理连续的大于(1)的数的话,最差最差是(O(nlog n))处理,发现很客观,那么最烦的(1)咋整?发现,如果遇到(1),可以分析此时(sum)和(prd)的性质,发现(prd)是不会变化的,但是(sum)就不一样,它一定是线性(+1)的数量,那么很显然,如果是一段连续的(1),设连续的(1)的数量为(cnt1)要确定是否存在答案,就看是否当前的和加上(1)的数量是否大于乘积的同时,当前和要小于乘积,这样一定在(1)的区间里面对答案造成贡献,即(sum + cnt1 >= prd)并且 (sum < prd),这样两个(for)循环嵌套就可以算完,最差的时间复杂度是(O(2· n ·log n )),就是那种一(2)隔着一个(1).
- 代码:
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 2e6 + 99;
typedef long long ll;
int a[N];
ll endOne[N];
ll usum[N];
signed main() {
ll n;
ios::sync_with_stdio(0);
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> a[i];
}
for (ll i = n; i >= 1; i--) {
usum[i] += usum[i+1] + a[i];
if (a[i] == 1) {
endOne[i] = max(i, endOne[i+1]);
}
else endOne[i] = -1;
}
ll ans = 0;
for (ll i = 1;i <= n; i++) {
ll prd = a[i];
ll sum = a[i];
for (ll j = i + 1; j <= n; j++) {
if (a[j] == 1) {
ll cnt1 = endOne[j] - j + 1;
j = endOne[j];
if (sum < prd && sum + cnt1 >= prd) {
ans++;
}
sum += cnt1;
continue;
}
prd *= a[j];
sum += a[j];
if (sum == prd) {
ans++;
}
if (usum[i + 1] + sum < prd)break;
}
}
cout << ans << endl;
}
- 题意:给(n < 10000)的数量的小数,精确到(6)位,给(m <= n)个询问,询问(l ~ r)区间内相同数的数量的最大值是否不小于 (lfloor frac{(r - l + 1)}{2} + 1
floor)
- 题解:看到(10000)就想想(n_{2})的暴力,毕竟少了个(0),还是经验不足啊,不敢写。给区间大致排个序然后双指针跑记录数量就行。
- 代码:
#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
int cnt[15000000];
const int N = 10009;
struct node {
int l, r, id;
}q[N];
bool cmp(node a, node b) {
if (a.l == b.l)return a.r < b.r;
return a.l < b.l;
}
int a[N];
int ans[N];
signed main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
double x;
cin >> x;
x *= 1000000;
a[i] = x;
}
for (int i = 1; i <= m; i++) {
int l, r;
cin >> l >> r;
q[i].l = l, q[i].r = r;
q[i].id = i;
}
sort(q + 1, q + 1 + m, cmp);
int l = 1, r = 1;
cnt[a[l]]++;
for (int i = 1; i <= m; i++) {
int L = q[i].l;
int R = q[i].r;
int id = q[i].id;
int Max = -1;
int cmp = (R - L + 1)/ 2 + 1;
while ( l < L) {
Max = max(--cnt[a[l++]],Max);
}while (l > L) {
Max = max(++cnt[a[--l]],Max);
}while (r > R) {
Max = max(--cnt[a[r--]],Max);
} while ( r < R) {
Max = max(++cnt[a[++r]],Max);
}
Max = -1;
for (int i = L; i <= R; i++) {
Max = max(Max, cnt[a[i]]);
}
if (Max >= cmp) {
ans[id] = 1;
}
}
for (int i =1;i <= m; i++) {
if (ans[i])cout << "usable
";
else cout << "unusable
";
}
}