3.5考试总结

A 狭窄的通道

题意:51nod 1331
题解:贪心
一种显然的想法是分成左中右三部分,左边退到左边再进去,右边同理,中间直接移,枚举左边和中间分界点
于是你获得了1/25的好成绩

1   
5 11        
5 3     
3 4     
1 10     
7 9     
9 1

这组数据答案是48,做法是先把前三个移到0,后两个移到L,再把1移到L,5移到0,最后左右分别移
所以还要一种贪心,枚举断点,左边全移0,右边全移L,分别按终点排序
然后设\(l,r\)为断点左边,右边的第一个
枚举直到发生冲突(左边的某一个的终点大于右边的)
avater
然后有两种方法

  • 左边的剩余的全部移到L
  • 右边的从上次移到这次移的全部移到0

第一种一次性把所有冲突解决了,直接更新
第二种不一定解决了所有冲突,需要等最后再更新
复杂度:\(O(n^2\log n)\)(应该吧)

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

inline void read(int& x)
{
    x = 0; char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define maxn 105
#define ll long long 
ll ans, sum;
int n, L;
struct Wolf
{
    int s, t;
}w[maxn];
inline bool cmp(Wolf& x, Wolf& y) { return x.s < y.s; }
inline bool cmp2(Wolf& x, Wolf& y) { return x.t < y.t; }
inline bool check(int l1, int r1, int l2, int r2)
{
    int mx = 0, mn = 1e9;
    for (int i = l1; i <= r1; ++i) mx = max(mx, w[i].t);
    for (int i = l2; i <= r2; ++i) mn = min(mn, w[i].t);
    return mx < mn;
}
void solve1()
{
    for (int i = 0; i <= n; ++i)
    {
        if (!check(0, i, i + 1, n)) continue;
        int cut = n + 1; sum = 0;
        for (int j = 0; j <= i; ++j) sum += w[j].s + w[j].t;
        for (int j = i + 1; j <= n; ++j)
        {
            if (!check(i + 1, j, j + 1, n))
            {
                cut = j;
                break;
            }
            sum += abs(w[j].s - w[j].t);
        }
        for (int j = cut; j <= n; ++j) sum += 2 * L - w[j].s - w[j].t;
        ans = min(ans, sum);
    }
}
void calc(int cut)
{
    sort(w + 1, w + cut + 1, cmp2);
    sort(w + cut + 1, w + n + 1, cmp2);
    int l = 1, r = cut + 1, rlast = cut + 1;
    ll sum1 = 0, sum2 = 0;
    while (l <= cut && r <= n)
    {
        sum2 = 0;
        while (l <= cut && w[l].t < w[r].t) ++l;
        if (l == cut + 1) break;
        while (r <= n && w[r].t < w[l].t) ++r;
        for (int i = l; i <= cut; ++i) sum2 += 2 * (L - w[i].t);//左边移L
        ans = min(ans, sum + sum1 + sum2);
        for (int i = rlast; i < r; ++i) sum1 += 2 * w[i].t;//右边移0
        if (w[r].t > w[cut].t) break;
        rlast = r, ++l;
    }
    ans = min(ans, sum + sum1);
}
void solve2()
{
    for (int i = 1; i < n; ++i)
    {
        sort(w + 1, w + n + 1, cmp);
        sum = 0;
        for (int j = 1; j <= i; ++j) sum += w[j].s + w[j].t;
        for (int j = i + 1; j <= n; ++j) sum += 2 * L - w[j].s - w[j].t;
        if (sum >= ans || check(1, i, i + 1, n)) continue;
        calc(i);
    }
}
int main()
{
    int T; read(T);
    while (T--)
    {
        read(n), read(L), ans = 1e18, sum = 0;
        for (int i = 1; i <= n; ++i) read(w[i].s), read(w[i].t);
        w[n + 1].s = w[n + 1].t = L;
        sort(w + 1, w + n + 1, cmp);
        solve1(); solve2();
        printf("%lld\n", ans);
    }
    return 0;
}

B 破坏道路

题意:51nod 1444
题解:最短路
预处理任意两点间的最短路
题意可以转化为至少需要多少条路才能达到要求
于是可以合并道路,具体看代码
注意这道题由于边数很少,dijskra会T掉
复杂度:\(O(n^2+n*spfa)\)

#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
    x = 0; char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define maxn 3005
#define mp make_pair
#define pa pair<int,int>
struct Edge
{
    int fr, to, val;
}eg[maxn << 1];
int head[maxn], edgenum, n, m, dis[maxn][maxn], vis[maxn];
int s1, t1, l1, s2, t2, l2;
inline void add(int fr, int to)
{
    eg[++edgenum] = { head[fr],to,1 };
    head[fr] = edgenum;
}
queue<int> q;
inline void spfa(int S)
{
    q.push(S);
    dis[S][S] = 0;
    while (!q.empty())
    {
        int tp = q.front();
        q.pop();
        vis[tp] = 0;
        for (int i = head[tp]; i; i = eg[i].fr)
            if (dis[S][eg[i].to] > dis[S][tp] + eg[i].val)
            {
                dis[S][eg[i].to] = dis[S][tp] + eg[i].val;
                if (!vis[eg[i].to]) vis[eg[i].to] = 1, q.push(eg[i].to);
            }
    }
}
inline void cmin(int& a, int b)
{
    a = min(a, b);
}
int main()
{
    read(n), read(m);
    for (int i = 1, u, v; i <= m; ++i)
        read(u), read(v), add(u, v), add(v, u);
    read(s1), read(t1), read(l1), read(s2), read(t2), read(l2);
    memset(dis, 0x3f, sizeof(dis));
    for (int i = 1; i <= n; ++i) spfa(i);
    if (dis[s1][t1] > l1 || dis[s2][t2] > l2)
    {
        puts("-1");
        return 0;
    }
    int ans = dis[s1][t1] + dis[s2][t2];
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
        {
            if (dis[s1][i] + dis[i][j] + dis[j][t1] <= l1 && dis[s2][i] + dis[i][j] + dis[j][t2] <= l2)
                cmin(ans, dis[s1][i] + dis[s2][i] + dis[i][j] + dis[j][t1] + dis[j][t2]);
            if (dis[s1][j] + dis[j][i] + dis[i][t1] <= l1 && dis[s2][i] + dis[i][j] + dis[j][t2] <= l2)//不写这个就只有85
                cmin(ans, dis[s1][j] + dis[j][i] + dis[i][t1] + dis[s2][i] + dis[j][t2]);
        }
    printf("%d\n", m - ans);
    return 0;
}

C 矩形面积交

题意:51nod 1302
题解:贪心
一组里的面积一定是最短长乘最短宽,一定是短边对短边,长边对长边
钦定最短短边放在A组里
按长边把剩下的排序
长边从大往小扔到B组里,满了就把短边最短的扔到A组里
注意A组的最短长度要特判一下
复杂度:\(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
    x = 0; char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define ll long long
#define mp make_pair
#define pa pair<int,int>
priority_queue<pa, vector<pa>, greater<pa> >q;
struct Rectangle
{
    int x, y;
    bool operator < (const Rectangle& p) const
    {
        if (y != p.y) return y < p.y;
        return x < p.x;
    }
}a[200005];
int n;
int main()
{
    read(n);
    for (int i = 1; i <= n * 2; ++i)
    {
        read(a[i].x), read(a[i].y);
        if (a[i].x > a[i].y) swap(a[i].x, a[i].y);
        if (a[i].x < a[1].x || (a[i].x == a[1].x && a[i].y < a[1].y)) swap(a[i], a[1]);
    }
    sort(a + 2, a + 2 * n + 1);
    ll ans = 0;
    for (int i = 2 * n; i >= 2; --i)
    {
        q.push(mp(a[i].x, a[i].y));
        if (q.size() > n)
        {
            a[1].y = min(a[1].y, q.top().second);
            q.pop();
        }
        if (q.size() == n)
        {
            int tmp = a[1].y;
            if (i != 2) tmp = min(tmp, a[2].y);//i=2时已经被分好组了,否则默认A组
            ans = max(ans, 1ll * a[1].x * tmp + 1ll * a[i].y * q.top().first);//A+B
        }
    }
    printf("%lld\n", ans);
    return 0;
}

D 两棵树的问题

题意:51nod 1325
题解:最大权闭合子图
可惜我考场上没想到啊
注意是无根树,所以要枚举根
或者也可以用点分治求出必选的重心这里
复杂度:\(O(n^4)/O(n^3\log n)\)(设最大流复杂度为\(n^3\)

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

inline void read(int& x)
{
    x = 0; char c = getchar(); int f = 1;
    while (!isdigit(c)) { if (c == '-') f = -1; c = getchar(); }
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
    x *= f;
}
#define maxn 100005
#define inf 0x3f3f3f3f
struct Edge
{
    int fr, to, val;
}eg[maxn << 1];
int head[maxn], edgenum = 1;
inline void add(int fr, int to, int val)
{
    eg[++edgenum] = { head[fr],to,val };
    head[fr] = edgenum;
}
#define Add(a,b,c) add(a,b,c),add(b,a,0)
#define to eg[i].to
int dep[maxn], vis[maxn], cur[maxn], S, T, maxflow;
queue<int> q;
bool bfs()
{
    for (int i = 0; i <= T; ++i) dep[i] = inf, cur[i] = head[i];
    while (!q.empty()) q.pop();
    dep[S] = 0; q.push(S);
    while (!q.empty())
    {
        int tp = q.front();
        q.pop(); vis[tp] = 0;
        for (int i = head[tp]; i; i = eg[i].fr)
            if (dep[to] > dep[tp] + 1 && eg[i].val)
            {
                dep[to] = dep[tp] + 1;
                if (!vis[to]) vis[to] = 1, q.push(to);
            }
    }
    return dep[T] != inf;
}
int dfs(int rt, int flow)
{
    if (rt == T)
    {
        maxflow += flow;
        return flow;
    }
    int tmpflow = 0, used = 0;
    for (int& i = cur[rt]; i; i = eg[i].fr)
        if (dep[to] == dep[rt] + 1 && eg[i].val)
            if (tmpflow = dfs(to, min(flow - used, eg[i].val)))
            {
                used += tmpflow;
                eg[i].val -= tmpflow;
                eg[i ^ 1].val += tmpflow;
                if (used == flow) break;
            }
    return used;
}
inline void dinic()
{
    while (bfs()) dfs(S, inf);
}
#undef to
int val[maxn];
struct Graph
{
    struct Edge
    {
        int fr, to;
    }eg[maxn << 1];
    int head[maxn], edgenum, fa[maxn];
    inline void add(int fr, int to)
    {
        eg[++edgenum] = { head[fr],to };
        head[fr] = edgenum;
    }
    inline void ADD(int fr, int to)
    {
        add(fr, to), add(to, fr);
    }
    void dfs(int rt)
    {
        for (int i = head[rt]; i; i = eg[i].fr)
            if (eg[i].to != fa[rt]) fa[eg[i].to] = rt, dfs(eg[i].to);
    }
}G[2];
int main()
{
    int n, ans = 0, sum = 0; read(n);
    S = n + 1, T = n + 2;
    for (int i = 1; i <= n; ++i) read(val[i]), sum += val[i] > 0 ? val[i] : 0;
    for (int i = 1, FR, TO; i < n; ++i) read(FR), read(TO), ++FR, ++TO, G[0].ADD(FR, TO);
    for (int i = 1, FR, TO; i < n; ++i) read(FR), read(TO), ++FR, ++TO, G[1].ADD(FR, TO);
    for (int rt = 1; rt <= n; ++rt)
    {
        memset(head, 0, sizeof(head)); edgenum = 1; maxflow = 0;
        for (int i = 1; i <= n; ++i)
        {
            if (val[i] > 0) Add(S, i, val[i]);
            if (val[i] < 0) Add(i, T, -val[i]);
        }
        G[0].fa[rt] = G[1].fa[rt] = 0;
        G[0].dfs(rt), G[1].dfs(rt);
        for (int i = 1; i <= n; ++i)
        {
            Add(i, G[0].fa[i], inf);
            Add(i, G[1].fa[i], inf);
        }
        dinic();
        ans = max(ans, sum - maxflow);
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/123789456ye/p/12435494.html