洛谷 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;
}
珍爱生命,远离抄袭!
原文地址:https://www.cnblogs.com/ycx-akioi/p/Luogu-P6775.html