0724提高組2-二分(直播)


備注:
對(duì)于30%的數(shù)據(jù)有N≤15。
對(duì)于70%的數(shù)據(jù)有N≤2000,M≤50000。
對(duì)于100%的數(shù)據(jù)有N≤20000,M≤100000。

算法知識(shí)點(diǎn):二分,染色法判斷二分圖
復(fù)雜度:O((N + M)logC)

解題思路:
將罪犯當(dāng)做點(diǎn),罪犯之間的仇恨關(guān)系當(dāng)做點(diǎn)與點(diǎn)之間的無向邊,邊的權(quán)重是罪犯之間的仇恨值。
那么原問題變成:將所有點(diǎn)分成兩組,使得各組內(nèi)邊的權(quán)重的最大值盡可能小。

我們?cè)?[0, 10^9]之間枚舉最大邊權(quán) limitlimit,當(dāng) limitlimit 固定之后,剩下的問題就是:

判斷能否將所有點(diǎn)分成兩組,使得所有權(quán)值大于 limitlimit 的邊都在組間,而不在組內(nèi)。也就是判斷由所有點(diǎn)以及所有權(quán)值大于 limitlimit 的邊構(gòu)成的新圖是否是二分圖。
判斷二分圖可以用染色法,時(shí)間復(fù)雜度是 O(N + M)O(N+M),其中 NN 是點(diǎn)數(shù),MM 是邊數(shù)。

為了加速算法,我們來考慮是否可以用二分枚舉 limit, 假定最終最大邊權(quán)的最小值是 Ans:
那么當(dāng) limit \in [ans, 10^9]limit∈[ans,10 9 ] 時(shí),所有邊權(quán)大于 limit 的邊,必然是所有邊權(quán)大于 Ans 的邊的子集,因此由此構(gòu)成的新圖也是二分圖。
當(dāng) limit∈[0,ans?1] 時(shí),由于 ans 是新圖可以構(gòu)成二分圖的最小值,因此由大于 limitlimit 的邊構(gòu)成的新圖一定不是二分圖。
所以整個(gè)區(qū)間具有二段性,可以二分出分界點(diǎn) ans 的值。
時(shí)間復(fù)雜度分析:

總共二分 logClogC 次,其中 CC 是邊權(quán)的最大值,每次二分使用染色法判斷二分圖,時(shí)間復(fù)雜度是 O(N + M),其中 N 是點(diǎn)數(shù),M 是邊數(shù)。因此總時(shí)間復(fù)雜度是 O((N + M)logC)。

代碼:

#include<stdlib.h>
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define oo INT_MAX
#define ll long long
#define db double
#define mp(a, b) make_pair(a, b)
#define met(a, b) memset(a, b, sizeof(a))
#define maxn 100000+10
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for(int i = (a); i >= (b); --i)
#define _for(i, a, b) for(int i = (a); i < (b) ;++i)
using namespace std;
const int N = 20010,M = 200010 ;

int n,m;
int h[N],e[M],w[M],ne[M],idx;
int color[N];
    
void add(int a,int b,int c)
{
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}
bool dfs(int u,int c,int limit)
{
    color[u]=c;
    for(int i=h[u];~i;i=ne[i])///
    {
        if(w[i] <= limit) continue;
        int j = e[i];
        if(color[j])
        {
            if(color[j]==c)
                return false;
        }
        else if(!dfs(j,3-c,limit)) 
            return false;
    }
    return true;
}

bool check(int limit)
{
    memset(color,0,sizeof color);
    for(int i=1;i<=n;i++)
    {
        if(color[i] == 0)
        {
            if(!dfs(i,1,limit))
                return false;
        }
    }
    return true;
}


int main()
{
    
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c),add(b,a,c);
    }

    int l = 0,r = 1e9;
    while(l<r)//二分
    {
        int mid = (l+r) >> 1;
        if(check(mid)) r = mid;
        else l = mid+1;
    }
    printf("%d\n",l);
    return 0;
}


方法:二分(算次數(shù))+差分


備注:
對(duì)于 10%的數(shù)據(jù),有 1 ≤ n,m ≤ 10;
對(duì)于 30%的數(shù)據(jù),有 1 ≤ n,m ≤ 500;
對(duì)于 50%的數(shù)據(jù),有 1 ≤ n,m ≤ 5,000;
對(duì)于 70%的數(shù)據(jù),有 1 ≤ n,m ≤ 10,000;
對(duì)于 100%的數(shù)據(jù),有 1 ≤ n,m ≤ 200,000,0 < wi, vi ≤ 106,0 < S ≤ 1012,1 ≤ Li ≤ Ri ≤ n。

#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cstdio>
using namespace std;

typedef long long ll;
const int N = 200020;
ll sum[N];
ll S;
int w[N],v[N];
int l[N],r[N];
int cnt[N];
int n,m;

ll get(int W)
{

    for(int i=1;i<=n;i++)
        if(w[i] >= W)
        {
            sum[i] = sum[i-1] + v[i];
            cnt[i] = cnt[i-1] + 1;
        }
        else
        {
            sum[i] = sum[i-1];
            cnt[i] = cnt[i-1];
        }
        ll res = 0;
        for(int i=0;i<m;i++)
        {
            res+=(cnt[r[i]]-cnt[l[i]-1])*(sum[r[i]]-sum[l[i]-1]);
        }
    return res;
}

int main()
{
    scanf("%d%d%lld",&n,&m,&S);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&w[i],&v[i]);
    for(int i=0;i<m;i++)
        scanf("%d%d",&l[i],&r[i]);

    int l=0,r=1e6+1;
    while(l<r)
    {
        int mid=l+r+1>>1;
        if(get(mid)>=S) l=mid;
        else r=mid-1;
    }
    printf("%lld\n",min(abs(get(r)-S),abs(S-get(r+1))));
    return 0;
}

示例1
輸入
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
輸出
-1
2
說明
第1 份訂單滿足后,4 天剩余的教室數(shù)分別為0,3,2,3。
第2 份訂單要求第2 天到第4 天每天提供3 個(gè)教室,而第3 天剩余的教室數(shù)為2,因此無法滿足。分配停止,通知第2個(gè)申請(qǐng)人修改訂單。

備注:
對(duì)于10%的數(shù)據(jù),有1≤n,m≤10;
對(duì)于30%的數(shù)據(jù),有1≤n,m≤1000;
對(duì)于70%的數(shù)據(jù),有1≤n,m≤105;
對(duì)于100%的數(shù)據(jù),有1≤n, m≤106, 0≤ri, dj≤109, 1≤sj≤tj≤ n。

怎么做:


#include<stdlib.h>
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
const int maxn=1000010;
int n,m;
int r[maxn],d[maxn],s[maxn],t[maxn];
ll b[maxn];

bool check(int k)
{
    for(int i=1;i<=n;i++)
        b[i] = r[i];
    for(int i=1;i<=k;i++)
    {
        b[s[i]] -=d[i];
        b[t[i]+1] += d[i];
    }

    ll res = 0;
    for(int i=1;i<=n;i++)
    {
        res+=b[i];
        if(res < 0)return true;
    }

    return false;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&r[i]);
    for(int i=n;i;i--)
        r[i] -= r[i-1];

    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&d[i],&s[i],&t[i]);

    int l=1,r=m;
    while(l<r)
    {
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }

    if(check(r))
    {
        puts("-1");
        printf("%d\n",r);
    }
    else puts("0");

    return 0;
}

示例1
輸入
25 5 2
2
11
14
17
21
輸出
4

說明
將與起點(diǎn)距離為 2 和14 的兩個(gè)巖石移走后,最短的跳躍距離為 4(從與起點(diǎn)距離17的巖石跳到距離 21的巖石,或者從距離 21 的巖石跳到終點(diǎn))。

備注:
對(duì)于20%的數(shù)據(jù),0 ≤ M ≤ N ≤ 10。
對(duì)于50%的數(shù)據(jù),0 ≤ M ≤ N ≤ 100。
對(duì)于100%的數(shù)據(jù),0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。

怎么做

#include<stdlib.h>
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
const int maxn=50010;
int L,n,m;
int d[maxn];

bool check(int mid)
{
    int last = 0,cnt = 0;
    for(int i=1;i<=n;i++)
        if(d[i]-last < mid) cnt++;
        else last = d[i];
    return cnt<=m;///
}

int main()
{
    scanf("%d%d%d",&L,&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&d[i]);
    d[++n] = L;

    int l=1,r=1e9;
    while(l<r)
    {
        int mid = l+r+1 >>1;
        if(check(mid)) l = mid;
        else r=mid - 1;
    }

    printf("%d\n",r);

    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容