【鸽巢原理】CF618F Double Knapsack

题目

求两可重集中元素和相等的两子集,元素大小不超过元素个数

题解

一晚上一开就是这道神题。。。
可以证明两个子集中一定存在都是连续子集的解:
S a i Sa_i Sai表示 a a a的前缀和
S b i Sb_i Sbi表示 b b b的前缀和
S a n > S b n Sa_n>Sb_n San>Sbn,
可以找到一个最大的 j j j满足 S a i ≥ S b j Sa_i\geq Sb_j SaiSbj,则由范围可得 0 ≤ S a i − S b j < n 0\leq Sa_i-Sb_j<n 0SaiSbj<n
于是 S a [ 0 , n ] Sa_{[0,n]} Sa[0,n] n + 1 n+1 n+1个数必定在 [ 0 , n − 1 ] [0,n-1] [0,n1] n n n个取值中有两个相同,得证。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+10;
int n,a[N],b[N];
ll sa[N],sb[N];
struct nd{
	ll num;int id1,id2;
	nd(ll x=0,int y=0,int z=0){num=x;id1=y;id2=z;}
}f[N];
bool operator<(const nd x,const nd y){return x.num<y.num;}
int main()
{
	bool ft=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),sa[i]=sa[i-1]+a[i];
	for(int i=1;i<=n;i++)scanf("%d",&b[i]),sb[i]=sb[i-1]+b[i];
	if(sa[n]<sb[n])swap(sa,sb),swap(a,b),ft=1;
	int p=0;
	for(int i=0;i<=n;i++)
	{
		while(p<n&&sb[p+1]<=sa[i])p++;
		f[i]=nd(sa[i]-sb[p],i,p);
	}
	sort(f,f+n+1);
	for(int i=0;i<n;i++)
	{
		if(f[i].num==f[i+1].num)
		{
			if(f[i].id1>f[i+1].id1)swap(f[i],f[i+1]);
			if(ft)swap(f[i].id1,f[i].id2),swap(f[i+1].id1,f[i+1].id2);
			printf(  "%d\n",f[i+1].id1-f[i].id1);
			for(int j=f[i].id1+1;j<=f[i+1].id1;j++)
			printf("%d ",j);
			printf("\n%d\n",f[i+1].id2-f[i].id2);
			for(int j=f[i].id2+1;j<=f[i+1].id2;j++)
			printf("%d ",j);
			return 0;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值