51nod 2564 格子染色

題目鏈接:http://www.51nod.com/Challenge/Problem.html#!#problemId=2564

AA爲染成白色的集合,BB爲染成黑色的集合 CC爲被懲罰的集合

ans=maxA,B(kAw(k))+kBb(k)kCP(i))=k=1n(w(k)+b(k))min(kAb(k)+kBw(k)+kCP(i))ans = \max_{A,B}\Big(\sum_{k\in A}w(k))+\sum_{k\in B}b(k)-\sum_{k\in C}P(i)\Big)\\ = \sum_{k=1}^n\Big(w(k)+b(k)\Big)-min\Big(\sum_{k\in A}b(k)+\sum_{k\in B}w(k) +\sum_{k\in C}P(i)\Big)
問題關鍵是極小化:
kAb(k)+kBw(k)+kCP(i)\sum_{k\in A}b(k)+\sum_{k\in B}w(k) +\sum_{k\in C}P(i)
那麼現在問題相當於最小化上述式子,其中,點ii選擇白色的點後,如果之前存在點kk有:k<il(i)a(k)r(i)k<i\\ l(i)\leq a(k) \leq r(i)
那麼這個點貢獻增加P(i)P(i)
考慮網絡流:
si,f=b(i)it,f=w(i)ii,f=P(i)ik,f=s\rightarrow i ,f=b(i)\\ i\rightarrow t ,f=w(i)\\ i\rightarrow i' ,f=P(i)\\ i'\rightarrow k ,f=∞
其中kk爲當ii選白色後,可以讓ii的貢獻提升的點。
這樣當運行最大流算法時:
如果w(i)b(i)w(i)\geq b(i),則b(i)b(i)所在邊一定會被充滿。
若此時,w(i)<b(i)w(i)<b(i),則p(i),w(i)p(i),w(i)至少有一條路線會滿流。
假設有p(i),b(i)p(i),b(i)都沒有滿,且所有的b(k)b(k)都滿.那麼此時所有w(k)w(k)一定也滿,這是因爲如果w(k)w(k)不滿,則矛盾,此時算法爲達到最大流。

這也就是說,當w(i)w(i)滿,b(i)b(i)不滿,若之前有b(k)b(k)滿,則w(k)w(k)一定滿,否則p(i)p(i)滿流。
考慮最大流最小割。此時相當於得到了最小割集。
flow=kAb(k)+kBw(k)+kCP(i)flow=\sum_{k\in A}b(k)+\sum_{k\in B}w(k) +\sum_{k\in C}P(i)

那麼:ans=k=1n(b(k)+w(k))flowans = \sum_{k=1}^n\Big(b(k)+w(k)\Big)-flow
由於nn太大了,建圖過於耗費內存。而多數重複的邊都是無窮流量的。所以可以把很多邊縮到一個點上,去聯接。對於某一塊mm個點與nn另外dd個點鏈接邊數可以變爲n+dn+d.
可以利用持久化線段時。並向所在區間的樹節點連邊。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <ctime>

const int maxn = 5005;
const int coe = 32;
const int INF = 0x3f3f3f3f;
using namespace std;
int maxFlow2(int source, int sink);
int maxFlow(int source, int sink);
struct node
{
    int c[2];
    node() { c[0] = c[1] = 0; }
};

struct edge_node
{
    int f, fir, nxt, to;
    edge_node() {}
};

int g_size = maxn*coe;
int Head[maxn*coe * 3];
node Node[maxn*coe];
edge_node EdgeNode[maxn*coe * 3];
int L[maxn];
int R[maxn];
int a[maxn];
int b[maxn];
int w[maxn];
int p[maxn];
int tmpMemory[maxn * 3];
int deep = 1;
int deep_edge;
int n, n2;
void addEdge_ex(int from, int to, int flow);
void addEdge(int from, int to, int flow = INF)
{
#ifdef _DEBUG
    if (from >= maxn*coe)printf("error\n");
    if (to >= maxn*coe)printf("error\n");
#endif // _DEBUG
    if (!flow)return;
    addEdge_ex(from, to, flow);
    addEdge_ex(to, from, 0);
}
void addEdge_ex(int from, int to, int flow)
{
    EdgeNode[deep_edge].f = flow;
    EdgeNode[deep_edge].to = to;
    EdgeNode[deep_edge].nxt = Head[from];
    if (Head[from] > -1)EdgeNode[Head[from]].fir = deep_edge;
    EdgeNode[deep_edge].fir = -1;
    Head[from] = deep_edge++;
}

void reduce(int from, edge_node &e)
{
    if (e.fir == -1)
        Head[from] = e.nxt;
    else
        EdgeNode[e.fir].nxt = e.nxt;
    if (e.nxt > -1)
        EdgeNode[e.nxt].fir = e.fir;
}

void addTree(int A, int l, int r, int last, int &k, int now, int pro = 0)
{
    if (!last)
    {
        if (A < l)return;
        if (A > r)return;
        if (!k)
        {
            k = deep++;
            if (pro)
                addEdge(pro + n2, k + n2);
        }
        if (l == r)
        {
            addEdge(k + n2, now);
            return;
        }
        int mid = (l + r) >> 1;
        addTree(A, l, mid, 0, Node[k].c[0], now, k);
        addTree(A, mid + 1, r, 0, Node[k].c[1], now, k);
        return;
    }
    
    if (A < l)return;
    if (A > r)return;
    if (!k)
    {
        k = deep++;
        if (pro)
            addEdge(pro + n2, k + n2);
    }
    if (l == r)
    {
        addEdge(k + n2, now);
        addEdge(k + n2, last + n2);
        return;
    }
    int mid = (l + r) >> 1;
    Node[k].c[!(A > mid)] = Node[last].c[!(A > mid)];
    if (Node[last].c[!(A > mid)])addEdge(k + n2, Node[last].c[!(A > mid)] + n2);
    if (A > mid)
        addTree(A, mid + 1, r, Node[last].c[1], Node[k].c[1], now, k);
    else
        addTree(A, l, mid, Node[last].c[0], Node[k].c[0], now, k);
    return;
}

void link_Tree_Edge(int root, int index, int l, int r, int nL, int nR)
{
    if (!root)return;
    if (nR < l)return;
    if (nL > r)return;
    if (l <= nL&&nR <= r)
    {
        addEdge(index, root + n2);
        return;
    }
    int mid = (nL + nR) >> 1;
    link_Tree_Edge(Node[root].c[0], index, l, r, nL, mid);
    link_Tree_Edge(Node[root].c[1], index, l, r, mid + 1, nR);
}

int main()
{
#ifdef _DEBUG
    //freopen("C:\\Users\\Administrator\\Downloads\\51nod_2564_4_in.txt", "r", stdin);
#endif
    
    //freopen("C:\\Users\\Administrator\\Downloads\\51nod_2564_9_in.txt", "r", stdin);
    memset(Head, -1, sizeof Head);
    int maxR = -1;
    scanf("%d", &n);
    n2 = n * 2;
#ifdef _DEBUG
    printf("n : %d\n", n);
#endif // _DEBUG
    long long total = 0;
#if 0
    n = 5000;
    n2 = n * 2;
    srand(213089139);
    for (int i = 0;i < n;i++)
    {
        if (i < n / 2)
        {
            b[i] = 20 + rand() % 10;
            w[i] = 33 + rand() % 10;
            p[i] = b[i] - w[i];
        }
        else
        {
            b[i] = w[i - n / 2];
            w[i] = b[i - n / 2];
            p[i] = b[i] - w[i];
        }
        tmpMemory[i * 3] = L[i] = 0;
        tmpMemory[i * 3 + 1] = R[i] = i;
        tmpMemory[i * 3 + 2] = a[i] = i % 2;
        total += b[i] + w[i];
    }
#else
    for (int i = 0;i < n;i++)
    {
        scanf("%d%d%d%d%d%d", a + i, b + i, w + i, L + i, R + i, p + i);
        tmpMemory[i * 3] = L[i];
        tmpMemory[i * 3 + 1] = R[i];
        tmpMemory[i * 3 + 2] = a[i];
        total += b[i] + w[i];
    }
    
#endif // _DEBUG
    
    
#ifdef _DEBUG
    printf("OK\n");
#endif // _DEBUG
    sort(tmpMemory, tmpMemory + n * 3);
    int count = int(unique(tmpMemory, tmpMemory + n * 3) - tmpMemory);
    for (int i = 0;i < n;i++)
    {
        L[i] = int(lower_bound(tmpMemory, tmpMemory + count, L[i]) - tmpMemory);
        R[i] = int(lower_bound(tmpMemory, tmpMemory + count, R[i]) - tmpMemory);
        a[i] = int(lower_bound(tmpMemory, tmpMemory + count, a[i]) - tmpMemory);
        if (maxR < L[i])maxR = L[i];
        if (maxR < R[i])maxR = R[i];
        if (maxR < a[i])maxR = a[i];
    }
    int source = maxn*coe - 1;
    int sink = maxn*coe - 2;
    int lastRoot = 0, newRoot = 0;
    
    for (int i = 0;i < n;i++, newRoot = 0)
    {
        addEdge(source, i, b[i]);
        addEdge(i, sink, w[i]);
        addEdge(i, i + n, p[i]);
        link_Tree_Edge(lastRoot, i + n, L[i], R[i], 0, maxR);
        addTree(a[i], 0, maxR, lastRoot, newRoot, i, 0);
        lastRoot = newRoot;
    }
    int debug;
    printf("%lld\n", total - (long long)(debug = maxFlow2(source, sink)));
    
#ifdef _DEBUG
    printf("%d\n", debug);
#endif // _DEBUG
    
}

int dst[maxn*coe], que[maxn*coe];
int dvis[maxn*coe];
int bfs_cnt = 1;
inline void addflow(int k, int flow) { EdgeNode[k].f += flow;    EdgeNode[k ^ 1].f -= flow; }
//578110485
//532140314

int DFS(int source, int flow, int sink, int d = 0)
{
    if (source == sink)return flow;
    if (dst[sink] == d)return 0;
    int sd = dst[source], fast = -1;
    for (int i =  Head[source];i > -1;i = EdgeNode[i].nxt)
    {
        edge_node &e = EdgeNode[i];
        if (e.f == 0)continue;
        if (dst[e.to] < sd + 1 || dvis[e.to] != bfs_cnt)
        {
            fast = i;
            continue;
        }
        int f = DFS(e.to, e.f > flow ? flow : e.f, sink, d + 1);
        if (f)
        {
            e.f -= f;
            if (e.f == 0)
            {
                if (fast == -1)
                    Head[source] = e.nxt;
                else
                    EdgeNode[fast].nxt = e.nxt;
            }
            if (EdgeNode[i ^ 1].f == 0)
            {
                EdgeNode[i ^ 1].nxt = Head[e.to];
                Head[e.to] = i ^ 1;
            }
            EdgeNode[i ^ 1].f += f;
            return f;
        }
        fast = i;
    }
    return 0;
}
#ifdef _DEBUG
int pro_index[maxn*coe];
#endif // _DEBUG

void BFS(int source, int sink)
{
    int size = deep + n2 + 10;
    int size_que = maxn*coe;
    /*memset(dst, 0x3f, size * sizeof(int));*/
    dst[source] = 0;
    dst[sink] = INF;
    dvis[source] = bfs_cnt;
    int l = 0, r = 1;
    que[0] = source;
    int disSink = 0;
#ifdef _DEBUG
    pro_index[source] = -1;
#endif // _DEBUG
    
    while (l != r)
    {
        int v = que[l++];
        if (l == size)l = 0;
        for (int i = Head[v];i > -1;i = EdgeNode[i].nxt)
        {
            edge_node &e = EdgeNode[i];
            if (e.f == 0)continue;
            if (dvis[e.to] == bfs_cnt)
            {
                //if (e.to == sink&&dst[e.to] > disSink)disSink = dst[e.to];
                continue;
            }
            dst[e.to] = dst[v] + 1;
            dvis[e.to] = bfs_cnt;
            que[r++] = e.to;
            if (r == size)r = 0;
        }
    }
    //if (disSink&&disSink != dst[sink])dst[sink] = (dst[sink] + disSink) / 2;
}

void BFS_2(int s)
{
    dst[s] = 0;
    int l = 0, r = 1;
    int siz = maxn*coe;
    que[0] = s;
    while (l < r)
    {
        int v = que[l++];
        if (l == siz)l = siz;
        for (int i = Head[v];i > -1;i = EdgeNode[i].nxt)
        {
            edge_node &e = EdgeNode[i];
            if (e.f)continue;
            if (dst[e.to] != INF)continue;
            dst[e.to] = dst[v] + 1;
            que[r++] = e.to;
            if (r == siz)r = siz;
        }
    }
    
#if 1
    for (int k = 0;k < siz;k++)
    {
        for (int i = Head[k];i > -1;i = EdgeNode[i].nxt)
        {
            edge_node &e = EdgeNode[i];
            if (e.f)continue;
            reduce(k, e);
        }
    }
#endif
    
}

int pro[maxn*coe];
int cur[maxn*coe];
int cnt[maxn*coe];
int maxFlow2(int source, int sink)
{
    memset(dst, 0x3F, sizeof dst);
    memset(cnt, 0, sizeof cnt);
    memcpy(cur, Head, maxn*coe * sizeof(int));
    dst[sink] = 0;
    int size = deep + n2 + 2;
    BFS_2(sink);
    for (int i = 0;i < maxn*coe;i++)if (dst[i] != INF)cnt[dst[i]]++;
    int flow = 0;
    int now = source;
    pro[now] = now;
    while (dst[source] < size)
    {
        int m = size - 1, flag = 1;
        if(now ==sink)
        {
            int f = EdgeNode[cur[source]].f, index = source, up = 1;
            for (int i = pro[now];i != source;i = pro[i])
            {
                edge_node &e = EdgeNode[cur[i]];
                if (e.f > f)continue;
                f = e.f;
                index = i;
            }
            if (f == EdgeNode[cur[source]].f)index = source;
            for (int i = pro[sink];;i = pro[i])
            {
                edge_node &e = EdgeNode[cur[i]];
                e.f -= f;
                if (e.f == 0)reduce(i, e);
                if (EdgeNode[cur[i] ^ 1].f == 0)
                {
                    int v = cur[i] ^ 1;
                    EdgeNode[v].nxt = Head[e.to];
                    if (Head[e.to] > -1)
                        EdgeNode[Head[e.to]].fir = v;
                    EdgeNode[v].fir = -1;
                    Head[e.to] = v;
                }
                EdgeNode[cur[i] ^ 1].f += f;
                if (up)cur[i] = Head[i];
                if (i == index)up = 0;
                if (i == source)break;
            }
            now = pro[index];
            flow += f;
        }
        for (int i = cur[now];i > -1;i = EdgeNode[i].nxt)
        {
            edge_node &e = EdgeNode[i];
            if (e.f == 0)continue;
            if (dst[e.to] + 1 != dst[now])continue;
            cur[now] = i;
            pro[e.to] = now;
            now = e.to;
            flag = 0;
            break;
        }
        if (flag)
        {
            cur[now] = Head[now];
            for (int i = cur[now];i > -1;i = EdgeNode[i].nxt)
            {
                edge_node &e = EdgeNode[i];
                if (e.f == 0)continue;
                if (dst[e.to] < m)m = dst[e.to];
            }
            --cnt[dst[now]];
            if (cnt[dst[now]] == 0)break;
            dst[now] = m + 1;
            cnt[dst[now]]++;
            now = pro[now];
        }
    }
    return flow;
}

int maxFlow(int source, int sink)
{
    int ret = 0, f = 1;
    double B = 0, D = 0;
    clock_t a, b, c;
    int dfs_cnt = 0;
    while (1)
    {
        a = clock();
        BFS(source, sink);
        b = clock() - a;
        B += double(b) / 1000;
        if (dvis[sink] != bfs_cnt)break;
        f = 1;
        a = clock();
        while (f)
        {
            ret += (f = DFS(source, INF, sink));
            ++dfs_cnt;
        }
        b = clock() - a;
        D += double(b) / 1000;
        ++bfs_cnt;
    }
    printf("D :%f\n B :%f\n bfs_cnt %d\n dfs_cnt %d\n", D, B, bfs_cnt, dfs_cnt);
    return ret;
}

/*
 20
 9 11 25 20 29 15
 13 16 27 2 37 9
 39 8 1 5 36 25
 13 38 9 29 35 1
 25 16 22 3 21 27
 28 9 33 1 26 29
 18 33 23 25 31 2
 1 9 25 3 21 39
 11 1 31 23 25 19
 26 1 23 1 6 26
 2 17 18 3 25 4
 6 1 11 11 38 22
 19 5 17 1 22 1
 22 26 13 18 21 27
 35 22 25 1 23 13
 1 4 13 7 21 21
 13 9 6 7 33 29
 19 11 1 16 30 1
 28 3 27 6 28 23
 21 39 7 21 33 39
 403
 5
 0 3 1 0 0 3
 1 3 2 0 1 3
 2 6 10 0 2 3
 3 5 0 0 3 3
 3 5 2 0 4 2
 5
 0 2 7 0 0 1
 0 5 2 0 1 5
 2 2 2 0 3 1
 2 3 2 0 3 1
 2 5 2 0 4 1
 
 */

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章