Wannafly挑战赛2

Wannafly挑战赛2

传送门

A Cut

签到题,没啥好说的。

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 7;

int a[N], n;

int main() {
    cin >> n;
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        sum += a[i];
    }
    sort(a + 1, a + n + 1);
    long long ans = 0;
    for (int i = 1; i < n; i++) {
        ans += 1ll*sum;
        sum -= a[i];
    }
    cout << ans << endl;
}

B Travel

题解:

这题有点坑。

刚开始每次询问按照 m m m的所有点建图然后跑迪杰斯特拉,超时, 复杂度是 q ∗ m 2 ∗ l o g ( n ) q * m ^ 2 * log(n) qm2logn

差不多是 2 e 9 2e9 2e9级别的被卡了。

这题可以 s s s t t t的求短路,只有三种情况, 要么直线从 s s s t t t,要么从 s s s 1 1 1再从 1 1 1 n n n, 然后再 n n n t t t

还有一种就是,通过这 m m m个点任意一个然后再从 这个点到 t t t

所有可以把有关这 m m m个点全部到所有点的最短距离预处理出来, 然后直接枚举每个点就好了。

最后复杂度为 q ∗ m + m ∗ n ∗ l o n g ( n ) q * m + m * n * long(n) qm+mnlong(n)

代码:

#include<bits/stdc++.h>

using namespace std;

const int N = 2e5 + 7;
typedef long long ll;

ll n, m, d[N];

ll sum[N];

struct edge{
    ll u, v, w;
}e[N];

struct node {
    ll to, w;
    bool operator < (const node x) const {
        return w > x.w;
    }
};


int vis[N];

int head[N], top = 1;

struct EDGE {
    int to, nxt;
    ll w;
}E[3 * N];


void add(int u, int v, int w) {
    E[top].to = v;
    E[top].w = w;
    E[top].nxt = head[u];
    head[u] = top++;
}

void add_edge(int u, int v, int w) {
    add(u, v, w);
    add(v, u, w);
}
 
vector<int> g;

int get_id(int x) {
    return lower_bound(g.begin(), g.end(), x) - g.begin() + 1;
}

ll dist[42][N];
 priority_queue<node> q;
void dj(int s, int id) {
   
    q.push({s, 0});
    for (int i = 1; i <= n; i++) {
        vis[i] = 0;
        dist[id][i] = 1e16;
    }
    dist[id][s] = 0;
    while (q.size()) {
        node cd = q.top();
        q.pop();
        if (vis[cd.to]) continue;
        vis[cd.to] = 1;
        for (int i = head[cd.to]; i; i = E[i].nxt) {
            int to = E[i].to;
            ll w = E[i].w;
            if (dist[id][to] > dist[id][cd.to] + w) {
                dist[id][to] = dist[id][cd.to] + w;
                q.push({to, dist[id][to]});
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> d[i];
        if (i + 1 <= n) {
            add_edge(i, i + 1, d[i]);
        } else {
            add_edge(i, 1, d[n]);
        }
       
    }

    for (int  i = 1; i < n; i++) {
        sum[i + 1] = sum[i] + d[i];
    }

    for (int i = 1; i <= m; i++) {
        cin >> e[i].u >> e[i].v >> e[i].w;
        g.push_back(e[i].u);
        g.push_back(e[i].v);
        add_edge(e[i].u, e[i].v, e[i].w);
    }
    sort(g.begin(),g.end());
    g.erase(unique(g.begin(), g.end()), g.end());

    for (int  i = 1; i <= m; i++) {
        dj(e[i].u, get_id(e[i].u));
        dj(e[i].v, get_id(e[i].v));
    }

    int q; cin >> q;
    while (q--) {
        int x, y; cin >> x >> y;
        if (y < x) swap(x, y);
        ll ans = sum[y] - sum[x];

        ans = min(ans, sum[x] + sum[n] - sum[y] + d[n]);

        for  (int i: g) {
            ans = min(ans, dist[get_id(i)][x] + dist[get_id(i)][y]);
        }

        cout << ans << endl;
    }
}

C Butterfly

题解:

这题可以设 d [ i ] [ j ] d[i][j] d[i][j] ( i , j ) (i, j) (i,j)这个位置开始向上做多有多少个连续的 x x x, 设 d l [ i ] [ j ] dl[i][j] dl[i][j]表示从 ( i , j ) (i, j) (i,j)开始是、左上角有多少个连续的, 设 d r [ i ] [ j ] dr[i][j] dr[i][j]为右上角有多少个连续的。

有了这个我们就可以枚举每个位置,求出答案。

#include <bits/stdc++.h>

using namespace std;
const int N = 2007;

char mp[N][N];

int d[N][N], dl[N][N], dr[N][N];

int lmin[N][N], rmin[N][N];

int main() {
   // ios::sync_with_stdio(0);
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            scanf(" %c", &mp[i][j]);
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (mp[i][j] == 'X') {
                d[i][j] = d[i - 1][j] + 1;
                dl[i][j] = dl[i - 1][j - 1] + 1;
            } else {
                d[i][j] = dl[i][j] = 0;
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = m; j; j--) {
            if (mp[i][j] == 'X') {
                dr[i][j] = dr[i- 1][j + 1] + 1;
            } else {
                dr[i][j] = 0;
            }
        }   
    }
   // cout << dl[5][5] << endl;

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            lmin[i][j] = min(d[i][j], dr[i][j]);
            rmin[i][j] = min(d[i][j], dl[i][j]);
        }
    }

    int ans = 0;

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (mp[i][j] != 'X') continue;
            if (lmin[i][j] % 2 == 0)lmin[i][j]--;
            for (int k = lmin[i][j]; k > ans; k--) {
                if (rmin[i][j + k - 1] >= k && k % 2) {
                    ans = max(ans, k);
                    break;
                }
            }
        }
    }
  //  if (ans == 1) ans = 0;
    cout << ans << endl;

}


D.Delete

题解:

首先是有向无环图, 删掉与这个点相连的所有边, 如何判断 s s s t t t的最短路是多少?

考虑拓扑排序, 如果删掉 x x x s s s t t t存在最短路,那么一定有一条边再的一个点小于 x x x的拓扑序, 和大于 x x x个拓扑序的点, 反之就不存在最短路。

那么如何查询最短路呢?

可以考虑用线段树。具体看代码吧。

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 7;


#define m (l + r) / 2
#define lson 2 * node
#define rson 2 * node + 1

typedef long long ll;
vector<pair<int, ll> > g[N];
int d[N], cat[N], top = 1;

ll tree[4 * N];

void tp(int n) {
    queue<int> q;
    for (int i = 1; i <= n; i++) {
        if (d[i] == 0) {
            q.push(i);
        }
    }
    while (q.size()) {
        int cd = q.front();
        q.pop();
        cat[cd] = top++;
        for (auto it: g[cd]) {
            int to = it.first;
            d[to]--;
            if (d[to] == 0) {
                q.push(to);
            }
        }

    }
}

ll dist[3][N], vis[N];

struct node {
    int to;
    ll w;
    bool operator <(const node  x) const {
        return w > x.w;
    }
};

priority_queue<node> q;


void dj(int s, int n, int id) {
    for (int i = 1; i <= n; i++) {
        vis[i] = 0;
        dist[id][i] = 1e15;
    }
    q.push({s, 0});
    dist[id][s] = 0;
    while (q.size()) {
        node cd = q.top();
        q.pop();
        if (vis[cd.to]) continue;  
        vis[cd.to] = 1;
        for (auto it: g[cd.to]) {
            int to = it.first;
            ll w = it.second;
            if (dist[id][to] > dist[id][cd.to] + w) {
                dist[id][to] = dist[id][cd.to] + w;
                q.push({to, dist[id][to]});
            }
        }
    }

}


void update(ll v, int ql, int qr, int l, int r, int node) {
    if (ql > qr) return;
    if (ql <= l && qr >= r) {
        tree[node] = min(tree[node], v);
        return ;
    } 
    if (ql <= m) update(v, ql, qr, l, m, lson);
    if (qr > m) update(v, ql, qr, m + 1, r, rson);
}

ll query(int pos, int l, int r, int node) {
    if (l == r) {
        return tree[node];
    }

    if (pos <= m) return min(tree[node], query(pos, l, m, lson));
    else return min(tree[node], query(pos, m + 1, r, rson));
}

struct edge{
    int u, v, w;
} e[N];

int main() {
    int n, M, s, t;
    scanf("%d %d %d %d", &n, &M, &s, &t);
    for (int i = 0; i < 4 * N; i++) {
        tree[i] = 1e16;
    }
    for (int i = 1; i <= M; i++) {
        int u, v, w; scanf("%d %d %d", &u, &v, &w);
        g[u].push_back({v, w});
    
        d[v]++;
        e[i].u = u, e[i].v = v, e[i].w = w;
    }
    tp(n);
 
    top--;
    dj(s, n, 1);
    for (int i = 1; i <= n; i++) {
        g[i].clear();
    }
    for (int i = 1; i <= M; i++) {
        g[e[i].v].push_back({e[i].u, e[i].w});
    }

    dj(t, n, 2);
    for (int i = 1; i <= M; i++) {
        int u = e[i].u, v = e[i].v;
        ll w = e[i].w + dist[1][u] + dist[2][v];
      //  cout << "l = " << cat[u] + 1 << " r = " << cat[v] - 1<< " w = " << w << endl;
        update(w, cat[u] + 1, cat[v] - 1, 1, top, 1);
    }
    int q; scanf("%d", &q);
    while (q--) {
        int x; scanf("%d", &x);
        if (dist[1][x] >= 1e15 || dist[2][x] >= 1e15) {
            printf("%lld\n", dist[1][t]);
            continue;
        }
        ll ans = query(cat[x] , 1, top, 1);
        if (ans >= 1e15) {
            puts("-1");
        } else {
            printf("%lld\n", ans);
        }

    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值