1 条题解

  • 0
    @ 2025-8-24 23:02:45

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar MattL
    卧室奶龙

    搬运于2025-08-24 23:02:45,当前版本为作者最后更新于2024-11-19 08:10:40,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    标签:二分、差分约束、图论建模、负环

    建议评紫。这种类似的题几乎都是紫。

    建议加强数据。才一个点,而且这个数据范围完全可以大很多


    题意描述地很清晰,除了格式有点丑,理解应该没有困难。

    观察题目,某个时间点的人数要大于等于要求人数。这需要用到不等式,容易想到差分约束。

    考虑如何建图。差分约束经典套路,先用前缀和表示。这样就能变成两个数的差而非一堆数的和。

    xix_i 表示第 ii 小时开始工作的人数,si=j=0ixjs_i=\sum_{j=0}^i x_j,可以写出不等式如:

    x1+x2+x3+x4+x5+x6+x7+x8R8x_1+x_2+x_3+x_4+x_5+x_6+x_7+x_8\geq R_8 s8s0R8s_8-s_0\geq R_8

    但是注意到 0 到 7 点很难处理,因为时间点形成一个环。不同于网络上大多数的做法断环为链,这里采用另一种方法。(断环为链简单介绍一下。就是把环复制两遍,这样尾接头的部分可以保证连续。)

    如:

    $$x_{20}+x_{21}+x_{22}+x_{23}+x_0+x_1+x_2+x_3\geq R_3 $$s23s19+s3R3s_{23}-s_{19}+s_3\geq R_3

    注意到,s23=anss_{23}=ans,即总雇佣人数。但我们求解答案的时候肯定无法得知答案,怎么办呢?

    容易想到二分答案。二分出一个 midmid,此时可以写出不等式:

    mids19+s3R3mid-s_{19}+s_3\geq R_3

    由于总数是 midmid,故还有条件:

    s23=mids_{23}=mid

    即:

    mids23midmid\leq s_{23}\leq mid

    同时,要使这个问题成立,使解有意义,还需满足一下不等式:

    0sisi1ai0\leq s_i-s_{i-1}\leq a_i

    aia_i 表示第 ii 个小时开始工作的有 aia_i 个人应聘)

    以上不等式均为两数之差(一个数的补足 0 个数的前缀和),按不等式建图差分约束即可。若跑出来没有负环则二分的这个答案成立。

    实现的时候注意用到的 0 个数的前缀和是 s1s_{-1},会爆下标,故所有下标 +1+1

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    #define ls (now<<1)
    #define rs (now<<1|1)
    #define LL long long
    #define f(i,n) for(int i=1;i<=n;i++)
    #define f_(i,n) for(int i=n;i>=1;i--)
    #define ff(i,a,b) for(int i=a;i<=b;i++)
    #define ff_(i,a,b) for(int i=a;i>=b;i--)
    #define pi pair<int,int> 
    #define pb push_back
    #define vi vector<int>
    #define yes cout<<"YES"<<endl
    #define no cout<<"NO"<<endl
    #define fs(i,s) for(int i=0;i<s.size();i++)
    #define re return
    #define con continue
    #define bre break
    #define mkp make_pair
    
    const int N=25;
    int r[N],a[N];
    int n=25,m,dis[N],cnt[N];
    vector<pi> to[N];
    bool vis[N];
    bool check(int k){
    	ff(i,0,24)to[i].clear();
    	f(i,24)to[i-1].pb(mkp(i,a[i-1])),to[i].pb(mkp(i-1,0));
    	ff(i,0,6)to[i+1].pb(mkp(i+24-8+1,k-r[i])); 
    	ff(i,7,23)to[i+1].pb(mkp(i-8+1,-r[i]));
    	to[0].pb(mkp(24,k));
    	to[24].pb(mkp(0,-k));
    	queue<int> q;
    	q.push(0);
    	memset(dis,63,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	memset(cnt,0,sizeof(cnt));
    	dis[0]=0;cnt[0]=1;vis[0]=1;
    	while(!q.empty()){//spfa
    		int k=q.front();q.pop();vis[k]=0;
    		for(auto i:to[k]){
    			if(dis[i.first]>dis[k]+i.second){
    				dis[i.first]=dis[k]+i.second;
    				if(!vis[i.first]){
    					cnt[i.first]++;vis[i.first]=1;
    					q.push(i.first);
    					if(cnt[i.first]>=n+1){
    						return 0;//有负环则不成立
    					}
    				}
    			}
    		}
    	}return 1;
    }
    void solve(){
    	ff(i,0,23)cin>>r[i],a[i]=0;
    	int m,tmp;
    	cin>>m;
    	f(i,m){
    		cin>>tmp;
    		a[tmp]++;
    	}int l=0,r=m+1;
    	while(l<r){
    		int mid=l+r>>1;
    		if(check(mid))r=mid;
    		else l=mid+1;
    	}if(l==m+1)cout<<"No Solution\n";
    	else cout<<l<<endl;
    }
    int main(){
        ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
        int T;
        cin>>T;
        while(T--){
        	solve();
    	}
        return 0;
    }
    
    • 1

    信息

    ID
    10138
    时间
    1000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者