“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛(同步赛) - G养花

链接:https://ac.nowcoder.com/acm/contest/5758/G
来源:牛客网

题目描述

小明是一个魔法师,他有n棵植物,所有植物排成一排,植物的初始高度为数组h,小明有一些强迫症,他想
让植物的高度都恰好达到k,小明有m瓶药水,但药水分为4种:
1.选择一棵高度为a0的植物变为b0高度的植物
2.选择一棵高度在[a1,a2]区间内的植物变为b1高度的植物
3.选择一棵高度为a1的植物变为[b1, b2]区间内某一高度的植物
4.选择一棵高度在[a1,a2]区间内的植物变为[b1,b2]区间内某一高度的植物
由于每瓶药水有C瓶库存,小明想知道他最多让多少棵植物高度达到k

输入描述:

输入数据第一行是t,表示数据的组数,接下来每组数据输入n,m,k,
接下来一行输入n个数,分别是每棵植物的高度h[i](1 <= h[i] < k),
接下来m行开头的两个数字为op和c表示药水是哪一种和该种药水有几瓶,输入如下
若 op=1,则接下来两个整数 a0,b0,意义如上文所述。
若 op=2,则接下来三个整数 a1,a2,b1,意义如上文所述。
若 op=3,则接下来三个整数 a1,b1,b2,意义如上文所述。
若 op=4,则接下来四个整数 a1,a2,b1,b2,意义如上文所述。
数据保证,所有 1 <= a0,b0,a1,b1,a2,b2 <= k
(t <= 10,n <= 1e4,m <= 300,k <= 100,c <=1e6)

输出描述:

输出一个整数,表示最多有多少颗植物能生涨到k。

思路:

算法:流量网络求最大流。

建图方法:

节点\(\mathit i\)代表高度为\(\mathit i\)的花

设节点\(S=0\)为源点,\(T=k+1\)为汇点。

用数组\(cnt_i\)记录最初有多少个高度为\(\mathit i\)的花,

源点和每一个高度节点可以建边:\((S,i,cnt_i)\)代表从\(\mathit S\)指向\(\mathit i\)流量为\(cnt_i\)的单向边。

四种药水也可以根据题意分别建立高度节点和高度节点之间的边。

最后建立高度为\(\mathit k\)的节点与汇点的边,流量设为无穷大。即:\((k,T,INF)\)

然后求\(S->T\)的最大流即可。

样例1的流量网络为:

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#include <sstream>
#include <bitset>
#include <unordered_map>
// #include <bits/stdc++.h>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define chu(x)  if(DEBUG_Switch) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) { if (a == 0ll) {return 0ll;} a %= MOD; ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
ll poww(ll a, ll b) { if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a ;} a = a * a ; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
inline long long readll() {long long tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
inline int readint() {int tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
void pvarr_int(int *arr, int n, int strat = 1) {if (strat == 0) {n--;} repd(i, strat, n) {printf("%d%c", arr[i], i == n ? '\n' : ' ');}}
void pvarr_LL(ll *arr, int n, int strat = 1) {if (strat == 0) {n--;} repd(i, strat, n) {printf("%lld%c", arr[i], i == n ? '\n' : ' ');}}
const int maxn = 100010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
#define DEBUG_Switch 0
#define N 1500
#define INF 1e13
struct Edge
{
    int from, to;
    ll  cap, flow; // cap 并不减少,只是flow在增,有残余就是cap>flow
};
struct ISAP
{
    int n, m, s, t;
    vector<Edge>edges;
    vector<int>G[N];
    bool vis[N];
    int d[N], cur[N];
    int p[N], num[N]; //比Dinic算法多了这两个数组,p数组标记父亲结点,num数组标记距离d[i]存在几个
    void addedge(int from, int to, ll cap)
    {
        // cout << from << " " << to << " " << cap << endl;
        edges.push_back((Edge) {from, to, cap, 0});
        edges.push_back((Edge) {to, from, 0, 0});
        int m = edges.size();
        G[from].push_back(m - 2);
        G[to].push_back(m - 1);
    }
    void init()
    {
        MS0(d);
        edges.clear();
        for (int i = 0; i <= n; ++i) {
            G[i].clear();
        }
    }
    ll Augumemt()
    {
        ll x = t, a = INF;
        while (x != s) //找最小的残量值
        {
            Edge&e = edges[p[x]];
            a = min(a, e.cap - e.flow);
            x = edges[p[x]].from;
        }
        x = t;
        while (x != s) //增广
        {
            edges[p[x]].flow += a;
            edges[p[x] ^ 1].flow -= a;
            x = edges[p[x]].from;
        }
        return a;
    }
    void bfs()//逆向进行bfs
    {
        memset(vis, 0, sizeof(vis));
        queue<int>q;
        q.push(t);
        d[t] = 0;
        vis[t] = 1;
        while (!q.empty())
        {
            int x = q.front(); q.pop();
            int len = G[x].size();
            for (int i = 0; i < len; i++)
            {
                Edge&e = edges[G[x][i]];
                if (!vis[e.from] && e.cap > e.flow)
                {
                    vis[e.from] = 1;
                    d[e.from] = d[x] + 1;
                    q.push(e.from);
                }
            }
        }
    }
 
    ll Maxflow(int s, int t) //根据情况前进或者后退,走到汇点时增广
    {
        this->s = s;
        this->t = t;
        ll flow = 0;
        bfs();
        memset(num, 0, sizeof(num));
        for (int i = 0; i < n; i++)
            num[d[i]]++;
        int x = s;
        memset(cur, 0, sizeof(cur));
        while (d[s] < n)
        {
            if (x == t) //走到了汇点,进行增广
            {
                flow += Augumemt();
                x = s; //增广后回到源点
            }
            int ok = 0;
            for (int i = cur[x]; i < G[x].size(); i++)
            {
                Edge&e = edges[G[x][i]];
                if (e.cap > e.flow && d[x] == d[e.to] + 1)
                {
                    ok = 1;
                    p[e.to] = G[x][i]; //记录来的时候走的边,即父边
                    cur[x] = i;
                    x = e.to; //前进
                    break;
                }
            }
            if (!ok) //走不动了,撤退
            {
                int m = n - 1; //如果没有弧,那么m+1就是n,即d[i]=n
                for (int i = 0; i < G[x].size(); i++)
                {
                    Edge&e = edges[G[x][i]];
                    if (e.cap > e.flow)
                        m = min(m, d[e.to]);
                }
                if (--num[d[x]] == 0)break; //如果走不动了,且这个距离值原来只有一个,那么s-t不连通,这就是所谓的“gap优化”
                num[d[x] = m + 1]++;
                cur[x] = 0;
                if (x != s)
                    x = edges[p[x]].from; //退一步,沿着父边返回
            }
        }
        return flow;
    }
    //  调用前给这里的n赋值,一般为n=T+1;
} gao;
 
 
int n;
int m, k;
int cnt[maxn];
 
// bool check(ll mid)
// {
//     gao.init();
//     int S = 0;
//     int T = n + 10;
//     gao.n = T + 1;
//     gao.addedge(n + i, T, d[i]);
//     ll res = gao.Maxflow(S, T);
// }
int main()
{
#if DEBUG_Switch
    freopen("C:\\code\\input.txt", "r", stdin);
#endif
    //freopen("C:\\code\\output.txt","r",stdin);
    int t;
    t = readint();
    while (t--)
    {
        n = readint();
        m = readint();
        k = readint();
        repd(i, 1, k)
        {
            cnt[i] = 0;
        }
        repd(i, 1, n)
        {
            int x = readint();
            cnt[x]++;
        }
        int S = 0;
        repd(i, 1, k)
        {
            gao.addedge(S, i , cnt[i] );
        }
        int id = k;
        repd(i, 1, m)
        {
            int op = readint();
            int c = readint();
            if (op == 1)
            {
                int a0 = readint();
                int b0 = readint();
                gao.addedge( a0 , b0, c );
            } else if (op == 2)
            {
                int a1 = readint();
                int a2 = readint();
                int b1 = readint();
                repd(a0, a1, a2)
                {
                    gao.addedge( a0, b1, c );
                }
            } else if (op == 3)
            {
                int a0 = readint();
                int b1 = readint();
                int b2 = readint();
                repd(b0, b1, b2)
                {
                    gao.addedge( a0, b0, c );
                }
            } else if (op == 4)
            {
                int a1 = readint();
                int a2 = readint();
                int b1 = readint();
                int b2 = readint();
                repd(a0, a1, a2)
                {
                    repd(b0, b1, b2)
                    {
                        gao.addedge( a0, b0, c );
                    }
                }
            }
        }
        id++;
        int T = id ;
        gao.addedge(k , T, INF );
        gao.n = T + 1;
        ll res = gao.Maxflow(S, T);
        gao.init();
        printf("%lld\n", res );
    }
 
    return 0;
}