正题
题目链接:https://uoj.ac/problem/750
题目大意
给出nnn个数字和一个ppp,保证2n>p2^n> p2n>p。现在要求一个序列www满足wi∈[−1,1]w_i\in[-1,1]wi∈[−1,1],使得∑i=1nwiai≡0(modp)\sum_{i=1}^nw_ia_i\equiv 0\pmod p∑i=1nwiai≡0(modp)
1≤p<2n,1≤n≤40,0≤ai<p1\leq p<2^n,1\leq n\leq 40,0\leq a_i<p1≤p<2n,1≤n≤40,0≤ai<p
解题思路
我们考虑从数字集合SSS中找两个数字和相同的集合T1,T2T_1,T_2T1,T2,那么T1−T1∩T2T_1-T_1\cap T_2T1−T1∩T2和T2−T1∩T2T_2-T_1\cap T_2T2−T1∩T2的和也相等,此时我们一边选111一边选−1-1−1即可,如果有一边是空的也行,这样另一边直接合法。
然后在SSS中选出集合的方案有2n2^{n}2n种,然后因为[0,p)[0,p)[0,p)有不超过这么多个数,所以肯定有重复的一个位置,所以肯定有解。
然后考虑怎么求这个解,看到这个范围我们考虑一下折半,我们搜出左右两边数字和的集合Sl,SrS_l,S_rSl,Sr。
如果左边或者右边有重复的就直接结束先,这样我们就能保证左右没有重复了,此时我们需要找到a,b∈Sl,c,d∈Sra,b\in S_l,c,d\in S_ra,b∈Sl,c,d∈Sr,使得a+c=b+da+c=b+da+c=b+d,因为两个集合的都很大,这个看起来很不可做。
但是我们知道一定有解,这个条件肯定是有用的,我们考虑二分一下这个和。每次分割成左右两个区间[l,mid],[mid+1,r][l,mid],[mid+1,r][l,mid],[mid+1,r],我们求出有多少对x∈Sl,y∈Srx\in S_l,y\in S_rx∈Sl,y∈Sr满足x+y∈[l,mid]x+y\in[l,mid]x+y∈[l,mid],如果超过mid−l+1mid-l+1mid−l+1那么答案肯定在左区间,否则在右区间。
时间复杂度:O(2n2n)O(2^{\frac{n}{2}}n)O(22nn)
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;
}