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


    正题

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


    题目大意

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

    1 ≤ p < 2 n , 1 ≤ n ≤ 40 , 0 ≤ a i < p 1\leq p<2^n,1\leq n\leq 40,0\leq a_i

    1p<2n,1n40,0ai<p


    解题思路

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

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

    然后考虑怎么求这个解,看到这个范围我们考虑一下折半,我们搜出左右两边数字和的集合 S l , S r S_l,S_r Sl,Sr

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

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

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


    code

    #include
    #include
    #include
    #include
    #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
    //	for(ll i=0;i
    //		ll x=(l+p-f[i])%p;
    //		if(mp[x]){
    //			mp[x]--;
    //			if(!mp[x]&&!i)continue;
    //		}
    //	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
  • 相关阅读:
    设计模式 -- 工厂模式
    【Java面试】线程状态,BLOCKED和WAITING有什么区别
    C语言代码质量与架构调整(三)
    PHP数组输出为xml的两种常见方法
    从JVM角度看继承
    GO远程构建并调试
    HiSilicon352 android9.0 emmc添加新分区
    openssl: issue-10463: 对/dev/random的依赖
    lwip_网卡
    JavaSE——数字格式化、产生随机数字、生成验证码
  • 原文地址:https://blog.csdn.net/Mr_wuyongcong/article/details/126272722