• 洛谷 P6775


    第 100 篇博客祭!UPD 2020.9.8:发现不是的,这是第 101 篇,之前写的 CF 809D 题解忘了分类了)

    话说这题为啥是个黑题啊,感觉我能独立 AC 的题都不能算黑题(

    然而在考场上还写挂了,有调味料选了 (0mathrm g),而输出格式规定 (y>0)(悲)。这启示我们下次写 spj 题的时候,如果自己写 checker 判正确性,一定要把题面里所有的限制都判一下……

    洛谷题目页面传送门

    题意见洛谷。

    首先,显然有 (sum d_i=mk)。以下设 (mn=min{d_i},mx=max{d_i}),则 (mxgeq dfrac mnk,mnleq dfrac mnk)

    注意到奇奇怪怪的数据范围,不妨先从 (m=n) 入手。此时显然有 (mxgeq k,mnleq k)。考虑配出一道菜,那么可以优先将 (mn) 用掉,然后用 (mx) 解决剩下的 (k-mn),由于 (mngeq1),所以一定有 (mx>k-mn)(mx) 用不完。可以保证恰好用掉一个原料。此时 (n,m) 都减一,转化为了一个规模小一级的 (m=n) 问题。边界是 (n=m=0)

    然后考虑 (m>n)。显然有 (mx>k)。直接用 (mx) 配出一个菜,还用不完,只有 (m) 减一。就这样一直减减减,直到 (m=n) 为止。

    然后考虑 (m=n-1)(m=1,n=2) 时直接配就可以了(这是边界)。否则,考虑配出一道菜。那么能否配出一道菜呢?这就要满足 (mx+mx'geq k),其中 (mx') 表示第二大的原料。肉眼只能得到 (mxgeq dfrac{n-1}nk) 这样一个条件,仿佛不能保证一定能配出。但是经过不懈的尝试,还是证出来了一定能。

    证明:反证法。假设不能,则 (mx+mx'<k),即 (mx'<k-mx)。又 (sum d_ileq (n-1)mx'+mx=u),显然当 (mx=dfrac{n-1}nk) 时,(u) 的上限最松为 (u<(n-1)left(k-dfrac{n-1}nk ight)+dfrac{n-1}nk=dfrac{2n-2}nk)。所以 (sum d_i<dfrac{2n-2}nk)。又 (n>2),所以 (dfrac2n<1),所以 (dfrac{2n-2}nk<(n-1)k=sum d_i),矛盾。得证。

    所以只需要优先用 (mx'),再用 (mx)。但是就怕 (mx'>k),用不完,那么用掉之后就 (m=n-2) 了,哦吼,未知问题。那怎么办呢?注意到显然有 (mn<k),先把 (mn) 用掉即可。这样可能会转化到一个规模小一级的 (m=n-1),也有可能 (m=n)(可能会 (mx+mx'=k))。

    至此,(mgeq n-1) 就归纳做完了。(m=n-2) 貌似没有啥思路?注意到可以将原料和菜分成两部分,使得每部分内部的原料和菜质量相等,并且 (m_1=n_1-1,m_2=n_2-1),这样就可以做了。然后猜想,若不能分,则无解,并试图证明。然后就证出来了(我考场上没证,直接写的)。

    证明:考虑证明逆否命题:若有解,则能分。考虑将每个菜看作节点,将每个原料也看作节点,若一个菜用了一个原料,则它们之间有边,构成一张二分图。显然,有 (n+m) 个点,最多有 (2m) 条边。要想图连通,必要条件是 (2mgeq n+m-1),即 (mgeq n-1)。现在 (m=n-2) 则图一定不连通。然后显然一定存在连通分量的 (m'=n'-1),因为全是 (m'geq n') 的话最终就 (mgeq n) 了。所以这个连通分量以及它的补图是独立的,各自有解。得证。

    接下来就是怎么分的事了。设分成两个集合 (S,T),则对 (A=S)(A=T) 都有 (sumlimits_{iin A}(d_i-k)=-k)。这是一个很简单的 01 背包,最终还原一下路径即可。定义域是 (mathrm O(nk)) 的,要更新 (n) 遍,复杂度 (mathrm O!left(n^2k ight)),吃不消。注意到 DP 数组只记录能否凑出的布尔值,转移时 bitset 移位 + 按位或即可。

    总时间复杂度 (mathrm O!left(dfrac{Tn^2k}w ight))

    代码(好像要加个 O3,然后过不过还要看评测只因心情。不过 ccf 只因应该更快吧,当时我查分的时候发现全是 PE 没有 T 的):

    #pragma GCC optimize(3)
    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    #define X first
    #define Y second
    const int inf=0x3f3f3f3f;
    const int N=500,K=5000;
    int n,m,k;
    int d[N+1];
    vector<pair<pair<int,int>,pair<int,int> > > ans;
    void use(int x,int y=0){
    	m--;
    	if(d[x]>=k){
    		ans.pb(mp(mp(x,k),mp(0,0)));
    		d[x]-=k;if(!d[x])n--;
    	}
    	else{
    		int now=k;
    		ans.pb(mp(mp(x,d[x]),mp(0,0)));
    		now-=d[x],d[x]=0,n--;
    		ans.back().Y=mp(y,now);
    		d[y]-=now;if(!d[y])n--;
    	}
    }
    void m_lss_n(){
    	pair<int,int> mn(inf,inf),mx1(0,0),mx2(0,0);
    	for(int i=1;i<=N;i++)if(d[i]){
    		mn=min(mn,mp(d[i],i));
    		if(mp(d[i],i)>mx1)mx2=mx1,mx1=mp(d[i],i);
    		else if(mp(d[i],i)>mx2)mx2=mp(d[i],i);
    	}
    	if(mx2.X>k)use(mn.Y,mx2.Y);
    	else use(mx2.Y,mx1.Y);
    }
    void m_eq_n(){
    	pair<int,int> mn(inf,inf),mx(0,0);
    	for(int i=1;i<=N;i++)if(d[i])mn=min(mn,mp(d[i],i)),mx=max(mx,mp(d[i],i));
    	use(mn.Y,mx.Y);
    }
    void m_grt_n(){
    	pair<int,int> mx(0,0);
    	for(int i=1;i<=N;i++)if(d[i])mx=max(mx,mp(d[i],i));
    	use(mx.Y);
    }
    void deal(){
    	while(m){
    		if(m<n)m_lss_n();
    		else if(m==n)m_eq_n();
    		else m_grt_n();
    	}
    }
    bitset<2*N*K+1> dp[N+1];
    void mian(){
    	ans.clear();
    	cin>>n>>m>>k;
    	memset(d,0,sizeof(d));
    	for(int i=1;i<=n;i++)cin>>d[i];
    	if(m>=n-1)deal();
    	else{
    		for(int i=0;i<=n;i++)dp[i].reset();
    		dp[0].set(n*k);
    		for(int i=1;i<=n;i++){
    			int x=d[i]-k;
    			if(x<0)dp[i]=dp[i-1]|dp[i-1]>>-x;
    			else dp[i]=dp[i-1]|dp[i-1]<<x;
    		}
    		vector<pair<int,int> > v1,v2;
    		int now=n*k-k;
    		if(!dp[n][now])return puts("-1"),void();
    		for(int i=n;i;i--){
    			if(dp[i-1][now])v1.pb(mp(i,d[i]));
    			else now-=d[i]-k,v2.pb(mp(i,d[i]));
    		}
    		memset(d,0,sizeof(d));
    		for(int i=0;i<v1.size();i++)d[v1[i].X]=v1[i].Y;
    		n=v1.size(),m=n-1,deal();
    		memset(d,0,sizeof(d));
    		for(int i=0;i<v2.size();i++)d[v2[i].X]=v2[i].Y;
    		n=v2.size(),m=n-1,deal();
    	}
    	for(int i=0;i<ans.size();i++)
    		if(ans[i].Y.Y)printf("%d %d %d %d
    ",ans[i].X.X,ans[i].X.Y,ans[i].Y.X,ans[i].Y.Y);
    		else printf("%d %d
    ",ans[i].X.X,ans[i].X.Y);
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    2019/9/8
    实现简单的网页登录注册功能 (使用html和css以及javascript技术) 没有美化的日后补全
    测试一些以前的代码
    使用三层开发遵循的原则
    超市管理
    热身训练
    考试第三题
    考试第七题
    考试第10题
    考试第8题
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/Luogu-P6775.html
Copyright © 2020-2023  润新知