2018 CCPC Final B - Balance of the Force(貪心+二分圖染色)

2018 CCPC Final B - Balance of the Force

題目鏈接:傳送門

題意:

給定 NN個人,每個人可以選擇加入黑暗 DarkDark 或者光明 LightLight 兩種陣營,他們加入不同的陣營之後獲得的力量值是不同的,即 DiD_iLiL_i 。然後有些人之間有矛盾,是不能加入同一陣營的,矛盾的對數共有 MM 對,現在給出所有的矛盾和所有的 Li,DiL_i,D_i,問在所有可能存在的最終陣營分配情況中,力量值最大的人和力量值最小的人之間的差最小可能是多少?如果說不可能分配的話輸出IMPOSSIBLE。其中 1N2105,,0M2105,,1LiDi1091\le N\le 2*10^5,,0\le M\le 2*10^5,,1\le L_i D_i \le 10^9 ,共20組test,時限4S

思路:

因爲每個聯通塊內對立的狀態已經給出,首先很容易想到對每個聯通塊進行二分圖染色,如果存在奇環就impossible,否則我們對二分圖賦予狀態(染色0的賦予狀態L和染色0的賦予狀態D),每種狀態會產生一個最大值和最小值。

如果沒有奇環,那麼現在問題就轉化爲有k堆物品,每堆物品有要麼取(maxx1,minn1)或(maxx2,minn2),現在要求如何對每堆物品賦予狀態使得k堆物品中maxx的最大值減去minn的最小值的差最小。

這個問題與另一個問題相似

n個物品,每個物品價值要麼取aia_i,要麼取bib_i ,你對這n個物品賦值,求這n堆物品中的最大值減最小值最大爲多少

這個問題的做法:我們首先稱aia_ibib_i是孿生的,即同一種類,但不可同時取。然後我們把所有aia_ibib_i 從小到大排序,然後依次枚舉最大值,然後用數據結構維護當前所有前面所有值的集合,更新答案。

我們從小到大遍歷價值:

  • 首先把該值插入到數據結構中
  • 如果至此出現種類數不夠n的話,就不更新答案,然後進行下面操作。
    • 如果該物品的孿生物品出現過的話,對孿生物品進行選擇,因爲現在只有最小值才能影響答案,從數據結構中刪除孿生物品中最小的那個,只留下大的那個.
    • 否則什麼也不做
  • 如果至此出現種類數夠n的話,更新答案
    • 如果孿生物品在前面出現過,那麼先將孿生物品從數據結構中刪除,然後更新答案(該值減去數據結構中的最小值),然後再將孿生物品插入進數據結構
    • 然後對孿生物品進行選擇,從數據結構中刪除孿生物品中最小的那個,只留下大的那個。

我們現在回到這個題,這個題跟上面基本一樣,枚舉最大值,更新答案,當出現孿生的話,我們選擇最小值大的那個。

  • 按照maxx值從小到大排序,然後用數據結構維護至此出現的minn
  • 每遍歷到一個物品就加入minn,如果出現孿生物品就選擇minn較大的那個(因爲之後的maxx,只有minn會影響答案)

代碼:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
vector<int> g[N];
int prices[N][2],color[N];
vector<int> seq;
struct State
{
    int maxx,minn,id;
    State() {}
    State(int maxx,int minn,int id):maxx(maxx),minn(minn),id(id) {}
    bool operator < (const State &o) const
    {
        return maxx<o.maxx;
    }
};
vector<State> t;
bool dfs(int u,int c)
{
    seq.push_back(u);
    color[u]=c;
    for(int v:g[u])
    {
        if(color[v]==-1)
        {
            if(!dfs(v,c^1))
                return false;
        }
        else
        {
            if(color[v]==c)
                return false;
        }
    }
    return true;
}
int lastpos[N];//lastpos[id], 種類id出現的數組下標pos
void set_delete(multiset<int> & mst,int v)
{
    auto it=mst.find(v);
    mst.erase(it);
}
void work(int n,int cas)//n種物品,每種物品兩個狀態,每個狀態屬性(maxx,minn)
{
    int cnt=0;
    fill(lastpos,lastpos+n+1,-1);
    multiset<int> mst;
    int ans=inf;
    for(int i=0; i<int(t.size()); ++i)
    {
        int tmaxx=t[i].maxx,tminn=t[i].minn,tid=t[i].id;
        mst.insert(tminn);
        if(lastpos[tid]==-1)
        {
            cnt++;//孿生沒有出現
            lastpos[tid]=i;
            if(cnt==n)
            {
                ans=min(ans,tmaxx-*mst.begin());
            }
        }
        else //孿生出現了
        {
            if(cnt==n)
            {
                set_delete(mst,t[lastpos[tid]].minn);
                ans=min(ans,tmaxx-*mst.begin());
                mst.insert(t[lastpos[tid]].minn);
            }
            set_delete(mst,min(t[lastpos[tid]].minn,tminn));
        }
    }
    printf("Case %d: %d\n",cas,ans);
}
int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; ++i)  g[i].clear();
        for(int i=1; i<=m; ++i)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        for(int i=1; i<=n; ++i)
            scanf("%d%d",&prices[i][0],&prices[i][1]);
        fill(color,color+n+1,-1);
        int kinds=0;
        bool isok=true;
        t.clear();
        for(int i=1; i<=n; ++i)
        {
            if(color[i]==-1)
            {
                seq.clear();
                kinds++;
                isok=dfs(i,0);
                if(!isok) break;
                int minn=inf,maxx=-1;
                for(int sign=0; sign<=1; ++sign)
                {
                    minn=inf,maxx=-1;
                    for(int a:seq)
                    {
                        if(color[a]==sign)
                        {
                            minn=min(minn,prices[a][0]);
                            maxx=max(maxx,prices[a][0]);
                        }
                        else
                        {
                            minn=min(minn,prices[a][1]);
                            maxx=max(maxx,prices[a][1]);
                        }
                    }
                    t.push_back(State(maxx,minn,kinds));
                }
            }
        }
        if(!isok)
        {
            printf("Case %d: IMPOSSIBLE\n",++cas);
            continue;
        }
        sort(t.begin(),t.end());
        work(kinds,++cas);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章