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

1|0正题

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


1|1题目大意

给出n个数字和一个p,保证2n>p。现在要求一个序列w满足wi[1,1],使得i=1nwiai0(modp)

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


1|2解题思路

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

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

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

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

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

时间复杂度:O(2n2n)


1|3code

#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; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/16573708.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(81)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2021-08-10 P7737-[NOI2021]庆典【tarjan,虚树】
2021-08-10 P5494-[模板]线段树分裂
2021-08-10 2021“MINIEYE杯”中国大学生算法设计超级联赛(7)部分题解
2021-08-10 jzoj7212-[2022省赛]染色(color)【根号分治】
点击右上角即可分享
微信分享提示