2020杭电多校第二场

1005 New_Equipments

对于每一条二次函数,选择前 n n n个最小的值对应的横坐标。该二次函数对应的 i i i,连向这n个点。再建立超级原点和超级终点。对于每个 k k k 1 < = k < = n 1<= k <= n 1<=k<=n),跑一次增广路,答案就是前缀和。

#include 

using namespace std;
typedef long long LL;
const int MAXN = 55;

struct Edge
{
    int from, to;
    LL cap, flow, cost;

    Edge(int u, int v, LL c, LL f, LL cc)
        : from(u), to(v), cap(c), flow(f), cost(cc) {}
};

const int maxn = 10001;
const int MAXM = 100001;
const LL INF = 6e18;
vector<LL> res;
struct MCMF
{
    int n, m;

    vector<Edge> edges;
    vector<int> G[maxn];
    int inq[maxn]; //是否在队列中
    LL d[maxn];    //bellmanford
    int p[maxn];   //上一条弧
    LL a[maxn];    //可改进量
    void init(int n)
    {
        this->n = n;
        for (int i = 0; i <= n; ++i)
            G[i].clear();
        edges.clear();
    }

    void addEdge(int from, int to, LL cap, LL cost)
    {
        edges.emplace_back(Edge(from, to, cap, 0, cost));
        edges.emplace_back(Edge(to, from, 0, 0, -cost));
        m = int(edges.size());
        G[from].emplace_back(m - 2);
        G[to].emplace_back(m - 1);
    }

    bool spfa(int s, int t, LL &flow, LL &cost)
    {
        for (int i = 1; i <= n; ++i)
            d[i] = INF;
        memset(inq, 0, sizeof(inq));
        d[s] = 0;
        inq[s] = 1;
        p[s] = 0;
        queue<int> q;
        a[s] = INF;
        q.push(s);
        while (!q.empty())
        {
            int u = q.front();
            q.pop();
            inq[u] = 0;
            for (int i = 0; i < int(G[u].size()); ++i)
            {
                Edge &e = edges[G[u][i]];
                if (e.cap > e.flow && d[e.to] > d[u] + e.cost)
                {
                    d[e.to] = d[u] + e.cost;
                    p[e.to] = G[u][i];
                    a[e.to] = min(a[u], e.cap - e.flow);
                    if (!inq[e.to])
                    {
                        q.push(e.to);
                        inq[e.to] = 1;
                    }
                }
            }
        }
        if (d[t] == INF)
            return false;
        flow += a[t];
        cost += (LL)d[t] * (LL)a[t];
        res.push_back(d[t]);
        for (int u = t; u != s; u = edges[p[u]].from)
        {
            edges[p[u]].flow += a[t];
            edges[p[u] ^ 1].flow -= a[t];
        }
        return true;
    }
    // int testnum = 1;
    LL MincostMaxflow(int s, int t, LL &cost)
    {
        LL flow = 0;
        cost = 0;
        // cout << "hello1\n";
        while (spfa(s, t, flow, cost))
        {
            // cout << "hello2\n";
            // cout << "testnum = " << testnum++ << '\n';
        }
        return flow;
    }
} mcmf;

LL n, m;
LL a[MAXN], b[MAXN], c[MAXN];
set<LL> allpoint;
map<LL, int> mpid;
LL nodeval[MAXN * MAXN];
struct Node
{
    LL p, val;
    bool operator<(const Node a) const
    {
        return val > a.val;
    }
};
int ju(LL A, LL B)
{
    if (B < (-2) * A * m)
    {
        return 3;
    }
    else if (B > (-2) * A)
    {
        return 1;
    }
    else
    {
        return 2;
    }
}
LL cal(LL A, LL B, LL j) { return A * j * j + B * j; }
vector<LL> getNode(LL A, LL B)
{
    vector<Node> vc;
    LL Z = -(B / (2 * A));
    int judge = ju(A, B);
    if (judge == 1)
    {
        for (LL i = 1; i <= n; i++)
        {
            vc.push_back({i, cal(A, B, i)});
            // cout << "i = " << i << '\n';
        }
    }
    else if (judge == 3)
    {
        for (LL i = m; i >= m - n + 1; i--)
        {
            vc.push_back({i, cal(A, B, i)});
        }
    }
    else
    {
        for (LL i = Z; i >= Z - n + 1 && i >= 1; i--)
            vc.push_back({i, cal(A, B, i)});
        for (LL i = Z + 1; i <= Z + n && i <= m; i++)
            vc.push_back({i, cal(A, B, i)});
    }
    sort(vc.begin(), vc.end(), [&](Node a, Node b) {
        return a.val < b.val;
    });
    vector<LL> ans;
    for (int i = 0; i < n; i++)
    {
        ans.push_back(vc[i].p);
        allpoint.insert(vc[i].p);
    }
    return ans;
}
vector<LL> ans[MAXN];
int main()
{
    // freopen("in.txt","r",stdin); //输入重定向,输入数据将从in.txt文件中读取
    // freopen("out.txt","w",stdout); //输出重定向,输出数据将保存在out.txt文件中
    // cout << 1 << '\n';
    // return 0;
    int T;
    scanf("%d", &T);
    while (T--)
    {
        // int testnum = 1;
        // cout << "testnum = " << testnum++ << '\n';
        res.clear();
        allpoint.clear();
        scanf("%lld %lld", &n, &m);
        for (int i = 1; i <= n; i++)
        {
            scanf("%lld %lld %lld", &a[i], &b[i], &c[i]);
        }

        // int Nodenum = n + int(allpoint.size()) + 10;
        // cout << "testnum = " << testnum++ << '\n';

        // cout << "testnum = " << testnum++ << '\n';
        for (int i = 1; i <= n; i++)
        {
            ans[i] = getNode(a[i], b[i]);
        }

        int cntnode = n;
        for (auto it : allpoint)
        {
            mpid[it] = ++cntnode;
            nodeval[cntnode] = it;
        }
        mcmf.init(cntnode + 3);
        for (int i = 1; i <= n; i++)
        {
            for (auto v : ans[i])
            {
                // cout << i << " " << mpid[v] << " " << cal(a[i], b[i], v) + c[i] << '\n';
                mcmf.addEdge(i, mpid[v], 1, cal(a[i], b[i], v) + c[i]);
            }
        }

        int S = cntnode + 1, T = cntnode + 2;
        for (int i = 1; i <= n; i++)
        {
            // cout << S << " " << i << '\n';
            mcmf.addEdge(S, i, 1, 0);
        }
        for (auto v : allpoint)
        {
            // cout << mpid[v] << " " << T << '\n';
            mcmf.addEdge(mpid[v], T, 1, 0);
        }
        // cout << "testnum = " << testnum++ << '\n';
        LL cost = 0;
        LL flow = mcmf.MincostMaxflow(S, T, cost);
        // cout << "flow = " << flow << '\n';
        // cout << "testnum = " << testnum++ << '\n';
        LL pre = 0;
        int lenres = res.size();
        for (int i = 0; i < lenres; i++)
        {
            pre += res[i];
            printf("%lld%c", pre, (i == lenres - 1 ? '\n' : ' '));
        }
    }
    // cin >> T;
}

1007 In Search of Gold

二分枚举可能的直径 m i d mid mid,每一次通过树形dp判断是否可能存在满足条件的选取方案。
f [ i ] [ j ] f[i][j] f[i][j]为以第 i i i个节点的子树有 j j j条边选取了 a a a边另外的边选取了 b b b边的离 i i i点最远的边的最小值,且要符合子树的直径符合条件。

#include 

using namespace std;
typedef long long LL;
const int MAXN = 2e4 + 5;
int N, K;
struct Node
{
    int to;
    LL a, b;
    Node(int _to, LL _a, LL _b)
    {
        to = _to;
        a = _a;
        b = _b;
    }
};
LL f[MAXN][22];
LL arrtmp[22];
int sz[MAXN];
vector<Node> edg[MAXN];
void dfs(int u, int fa, LL limi)
{
    sz[u] = 0;
    for (int i = 0; i <= K; i++)
        f[u][i] = 0;
    for (auto v : edg[u])
    {
        if (v.to == fa)
            continue;
        dfs(v.to, u, limi);
        int num = min(sz[u] + sz[v.to] + 1, K);
        for (int i = 0; i <= K; i++)
            arrtmp[i] = limi + 1;
        for (int j = 0; j <= sz[u]; j++)
            for (int k = 0; k + j <= K && k <= sz[v.to]; k++)
            {
                if (f[u][j] + f[v.to][k] + v.a <= limi)
                    arrtmp[j + k + 1] = min(arrtmp[j + k + 1], max(f[u][j], f[v.to][k] + v.a));
                if (f[u][j] + f[v.to][k] + v.b <= limi)
                    arrtmp[j + k] = min(arrtmp[j + k], max(f[u][j], f[v.to][k] + v.b));
            }
        for (int i = 0; i <= K; i++)
            f[u][i] = arrtmp[i];
        sz[u] = num;
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d %d", &N, &K);
        for (int i = 1; i <= N; i++)
            edg[i].clear();
        int u, v;
        LL a, b;
        LL L = 0, R = 0;
        for (int i = 1; i <= N - 1; i++)
        {
            scanf("%d %d %lld %lld", &u, &v, &a, &b);
            edg[u].push_back(Node(v, a, b));
            edg[v].push_back(Node(u, a, b));
            R += max(a, b);
        }

        LL ans = R;
        while (L <= R)
        {
            LL mid = (L + R) >> 1;
            dfs(1, 0, mid);
            // cout << "mid =  " << mid << '\n';
            if (f[1][K] <= mid)
            {
                R = mid - 1;
                ans = mid;
            }
            else
            {
                L = mid + 1;
            }
        }
        printf("%lld\n", ans);
    }
    // cin >> T;
}

1012 String Distance

Shortest judge solution: 879 Bytes.
考虑修改完毕后的A 串和B 串,它们对应位置的字符都相等,对于每一位:
• 如果两个串在这一位都做了插入操作,那么可以同时不做插入操作使得操作 次数减少2。
• 如果一个串A 在这一位做了插入操作,另一个串B 这一位不动,那么可以通过A 这一位不动,B 删除这一位达到同样的效果。
因此可以发现插入操作是没用的,所以两个串A 和B 的距离等于 ∣ A ∣ + ∣ B ∣ − 2 ∗ L C S ( A , B ) |A| + |B| - 2 * LCS(A, B) A+B2LCS(A,B),其中LCS 表示最长公共子序列。预处理出 g [ i ] [ j ] g[i][j] g[i][j] 表示 A [ i . . . n ] A[i...n] A[i...n] 里字符 j j j最早出现的下标。对于每个询问通过DP 求出LCS,设 f [ i ] [ j ] f[i][j] f[i][j] 表示与 B [ 1... i ] B[1...i] B[1...i] 的公共序列长度达到 j j j A [ l . . . r ] A[l...r] A[l...r]的最短
前缀的长度,利用 g g g数组进行转移。

#include 

using namespace std;

const int MAXN = 100005;
const int MAXM = 30;
char a[MAXN], b[MAXM];
int A[MAXN], B[MAXM];
int g[MAXN][MAXM];
int lena, lenb;
inline void up(int &a, int b) { a > b ? (a = b) : 0; }
void getp()
{
    for (int i = 0; i < 26; i++)
    {
        g[lena + 1][i] = lena + 1;
    }
    for (int i = lena; i > 0; i--)
    {
        for (int j = 0; j < 26; j++)
        {
            g[i][j] = g[i + 1][j];
        }
        g[i][A[i]] = i;
    }
}
int f[MAXM][MAXM];
int cal(int l, int r)
{
    for (int i = 0; i <= lenb; i++)
    {
        for (int j = 0; j <= i; j++)
        {
            f[i][j] = MAXN;
        }
    }
    f[0][0] = l - 1;
    for (int i = 0; i < lenb; i++)
    {
        for (int j = 0; j <= i; j++)
        {
            up(f[i + 1][j], f[i][j]);
            if (f[i][j] < r)
            {
                up(f[i + 1][j + 1], g[f[i][j] + 1][B[i + 1]]);
            }
        }
    }
    for (int j = lenb; j >= 0; j--)
    {
        if (f[lenb][j] <= r)
            return j;
    }

    return 0;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%s %s", a + 1, b + 1);
        lena = strlen(a + 1), lenb = strlen(b + 1);
        for (int i = 1; i <= lena; i++)
        {
            A[i] = a[i] - 'a';
        }
        for (int i = 1; i <= lenb; i++)
        {
            B[i] = b[i] - 'a';
        }
        getp();
        int q;
        scanf("%d", &q);
        while (q--)
        {
            int l, r;
            scanf("%d %d", &l, &r);
            printf("%d\n", r - l + 1 + lenb - 2 * cal(l, r));
        }
    }
}

你可能感兴趣的:(2020HDU多校)