ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

uoj#750-[UNR #6]小火车【二分,折半,鸽笼原理】

2022-08-10 20:01:35  阅读:170  来源: 互联网

标签:折半 UNR 750 int ll mid -- while include


正题

题目链接:https://uoj.ac/problem/750


题目大意

给出\(n\)个数字和一个\(p\),保证\(2^n> p\)。现在要求一个序列\(w\)满足\(w_i\in[-1,1]\),使得\(\sum_{i=1}^nw_ia_i\equiv 0\pmod p\)

\(1\leq p<2^n,1\leq n\leq 40,0\leq a_i<p\)


解题思路

我们考虑从数字集合\(S\)中找两个数字和相同的集合\(T_1,T_2\),那么\(T_1-T_1\cap T_2\)和\(T_2-T_1\cap T_2\)的和也相等,此时我们一边选\(1\)一边选\(-1\)即可,如果有一边是空的也行,这样另一边直接合法。

然后在\(S\)中选出集合的方案有\(2^{n}\)种,然后因为\([0,p)\)有不超过这么多个数,所以肯定有重复的一个位置,所以肯定有解。

然后考虑怎么求这个解,看到这个范围我们考虑一下折半,我们搜出左右两边数字和的集合\(S_l,S_r\)。

如果左边或者右边有重复的就直接结束先,这样我们就能保证左右没有重复了,此时我们需要找到\(a,b\in S_l,c,d\in S_r\),使得\(a+c=b+d\),因为两个集合的都很大,这个看起来很不可做。

但是我们知道一定有解,这个条件肯定是有用的,我们考虑二分一下这个和。每次分割成左右两个区间\([l,mid],[mid+1,r]\),我们求出有多少对\(x\in S_l,y\in S_r\)满足\(x+y\in[l,mid]\),如果超过\(mid-l+1\)那么答案肯定在左区间,否则在右区间。

时间复杂度:\(O(2^{\frac{n}{2}}n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
const ll N=45,M=1<<20;
ll n,p,L1,L2,a[N];
map<ll,ll> mp;pair<ll,ll> f[M],g[M];
bool check(ll l,ll r){
	int L=0,R=0;ll ans=0;
	for(int i=L1-1;i>=0;i--){
		while(R<L2&&f[i].first+g[R].first<=r)R++;
		while(L<L2&&f[i].first+g[L].first<l)L++;
		ans+=R-L;
	}
	L=0;R=0;
	for(int i=L1-1;i>=0;i--){
		while(R<L2&&f[i].first+g[R].first-p<=r)R++;
		while(L<L2&&f[i].first+g[L].first-p<l)L++;
		ans+=R-L;
	}
	return ans>(r-l+1);
}
void solve(ll ansL,ll ansR){
	ll k=ansL&ansR;ansL-=k;ansR-=k;
	for(int i=0;i<n;i++){
		if((ansL>>i)&1)printf("1 ");
		else if((ansR>>i)&1)printf("-1 ");
		else printf("0 ");
	}
	return;
}
signed main()
{
	scanf("%lld%lld",&n,&p);
	for(ll i=0;i<n;i++)scanf("%lld",&a[i]);
	L1=(1<<n/2);
	for(int s=1;s<L1;s++){
		for(int i=0;i<n/2;i++)
			if((s>>i)&1)(f[s].first+=a[i])%=p;
		f[s].second=s;
	}
	L2=(1<<n-n/2);
	for(int s=1;s<L2;s++){
		for(int i=0;i<(n-n/2);i++)
			if((s>>i)&1)(g[s].first+=a[i+n/2])%=p;
		g[s].second=s;
	}
	sort(f+1,f+L1);sort(g+1,g+L2);
	for(int i=1;i<L1-1;i++)if(f[i].first==f[i+1].first){solve(f[i].second,f[i+1].second);return 0;}
	for(int i=1;i<L2-1;i++)if(g[i].first==g[i+1].first){solve(g[i].second<<(n/2),g[i+1].second<<(n/2));return 0;}
			
	ll l=0,r=p-1;
	while(l<r){
		ll mid=(l+r)>>1;
		if(check(l,mid))r=mid;
		else l=mid+1;
	}
	
	ll z=0,ansL=0,flag=0;
	for(int i=L1-1;i>=0;i--){
		while(z<L2&&f[i].first+g[z].first<r)z++;
		if(f[i].first+g[z].first==r){
			if(!flag)ansL=f[i].second+(g[z].second<<n/2),flag=1;
			else{solve(ansL,f[i].second+(g[z].second<<n/2));return 0;}
		}
	}
	z=0;
	for(int i=L1-1;i>=0;i--){
		while(z<L2&&f[i].first+g[z].first-p<r)z++;
		if(f[i].first+g[z].first-p==r){
			if(!flag)ansL=f[i].second+(g[z].second<<n/2),flag=1;
			else{solve(ansL,f[i].second+(g[z].second<<n/2));return 0;}
		}
	}
	
//	for(ll i=0;i<L2;i++)mp[g[i]]=i+1;
//	for(ll i=0;i<L1;i++){
//		ll x=(l+p-f[i])%p;
//		if(mp[x]){
//			mp[x]--;
//			if(!mp[x]&&!i)continue;
//		}
//	}
	return 0;
}

标签:折半,UNR,750,int,ll,mid,--,while,include
来源: https://www.cnblogs.com/QuantAsk/p/16573708.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有