P10200 花神诞日 题解

news/2025/1/11 15:27:21/文章来源:https://www.cnblogs.com/dccy/p/18665747

P10200 [湖北省选模拟 2024] 花神诞日 题解

首先注意到一个集合中两两异或和的最小值就是,排序后相邻两个数异或和的最小值。证明可以考虑放到 01-Trie 上,从高往低位建树,求一个数与之异或的最小值,就是使高位相同位数尽可能多,则就是 01-Trie 上的前一个叶子或后一个叶子。

由此,我们可以设一个 \(O(n^2)\) 的 DP,把 \(a\) 排序后,设 \(f_{i,j}\) 表示考虑完了前 \(\max(i,j)\) 个数后,第一道菜考虑最后选了 \(i\),第二道菜最后选了 \(j\) 的方案数。转移就是考虑下一位与当前位是否属于同一道菜。这样我们能写出以下代码并获得 32 分。

const int mod=1e9+7,N=5e3+5;
ll a[N],k1,k2;
int n,f[N][N];
void add(int &x,int y) {x+=y;if(x>=mod) x-=mod;
}
signed main(){read(n,k1,k2);fo(i,1,n) read(a[i]);sort(a+1,a+1+n);f[1][0]=f[0][1]=1;fo(i,2,n) {fu(j,0,i-1) {if((a[i]^a[i-1])>=k1) add(f[i][j],f[i-1][j]);if((a[i]^a[i-1])>=k2) add(f[j][i],f[j][i-1]);if(!j||(a[i]^a[j])>=k1) add(f[i][i-1],f[j][i-1]);if(!j||(a[i]^a[j])>=k2) add(f[i-1][i],f[i-1][j]);}}int ans=0;fu(i,1,n) add(ans,f[n][i]),add(ans,f[i][n]);write(ans);return 0;
}

这样的状态不太简洁,事实上我们可以设 \(f_{i,j,0/1}\) 表示考虑完前 \(i\) 个位置,上一个与 \(i\) 颜色不同的位置是 \(j(j<i)\),且第 \(i\) 位属于第一道还是第二道菜的方案数。这与上面是等价的。则有如下转移(从 \(1\) 转移是对称的):

\[f_{i,j,0}\to f_{i+1,j,0}(a_{i+1}\oplus a_{i}\ge k_1) \]

\[f_{i,j,0}\to f_{i+1,i,1}(a_{i+1}\oplus a_j\ge k_2\lor j=0) \]

其中初始状态为 \(f_{1,0,0}=f_{1,0,1}=1\)

考虑优化 DP。发现第一条转移与 \(j\) 无关,我们可以直接继承。而第二条转移仅仅是把满足条件的 \(j\) 求和,可以把 DP 数组中的每个数放在 01-Trie 上,动态开点存在 \(a_j\) 代表的叶子中。由于条件是 \(a_{i+1}\oplus a_j\ge k_2\) 所以可以 \(O(\log V)\) 求和。时间复杂度 \(O(n\log V)\)

AC 代码,注意最后要减掉其中一道菜不选的情况:

const int mod=1e9+7,N=2e5+5;
const int L=1.5e7;
int n;
ll a[N],k1,k2;
void add(int &x,int y) {x+=y;if(x>=mod) x-=mod;
}
struct trie {int tot,tr[L][2],s[L];void insert(int &x,ll w,int v,int val) {if(!x) x=++tot,tr[x][0]=tr[x][1]=s[x]=0;if(v<0) {add(s[x],val); return;}insert(tr[x][w>>v&1],w,v-1,val);s[x]=(s[tr[x][0]]+s[tr[x][1]])%mod;}int query(int x,ll w1,ll w2,int v) {if(!x) return 0;if(v<0) return s[x];int o1=w1>>v&1,o2=w2>>v&1;if(o2) return query(tr[x][o1^1],w1,w2,v-1);else return (query(tr[x][o1],w1,w2,v-1)+s[tr[x][o1^1]])%mod;}int q0(int x,int v) {if(!x) return 0;if(v<0) return s[x];return q0(tr[x][0],v-1);}
}t1,t2;
int rt1,rt2;
signed main(){read(n,k1,k2);fo(i,1,n) read(a[i]);sort(a+1,a+1+n);t1.insert(rt1,0,59,1),t2.insert(rt2,0,59,1);fo(i,2,n) {int s1=t1.query(rt1,a[i],k2,59);int s2=t2.query(rt2,a[i],k1,59);if(a[i]<k2) add(s1,t1.q0(rt1,59));if(a[i]<k1) add(s2,t2.q0(rt2,59));if((a[i]^a[i-1])<k1) rt1=0,t1.tot=0;if((a[i]^a[i-1])<k2) rt2=0,t2.tot=0;t1.insert(rt1,a[i-1],59,s2),t2.insert(rt2,a[i-1],59,s1);}write(((ll)mod+t1.s[rt1]+t2.s[rt2]-t1.q0(rt1,59)-t2.q0(rt2,59))%mod);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/867784.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Text-Switch的练习1

Text-Switch的练习1 键盘录入一个从一到七的数字表示星期 星期一到星期五是工作日 星期六和星期日是休息日 Scanner xq=new Scanner(System.in);System.out.println("请输入一个一到七之间的数字");int week=xq.nextInt();switch(week){case 1 :case 2 :case 3 :case…

Omnissa Dynamic Environment Manager 2412 - 个性化动态 Windows 桌面环境管理

Omnissa Dynamic Environment Manager 2412 - 个性化动态 Windows 桌面环境管理Omnissa Dynamic Environment Manager 2412 - 个性化动态 Windows 桌面环境管理 Simplify management of user profiles, environment settings, and policies across desktops and apps. 请访问原…

stata 检查哪些变量有缺失值

ssc install nmissing nmissing 说明这十个变量有缺失

Text-IfElse的练习1

Text-IfElse的练习1 某商场有vip的会员制,根据不同的会员会有不同的折扣,假设商品总价为1000,键盘录入会员级别,并计算出最终支付的钱 一级会员打九折;二级会员打八折;三级会员打七折;不是会员原价,打骨折int price=1000;Scanner mall=new Scanner(System.in);System.o…

Text-While循环的练习1

Text-While循环的练习1 判断一个数是否为回文数 回文数规则为:回文数就是一个数倒过来跟它本身相同,例如121,212 Scanner ba=new Scanner(System.in);System.out.println("请输入一个数字:");int i=ba.nextInt();int k=i;int num=0;while(k!=0){int g=k%10;//分离出…

Text-For循环的练习1

Text-For循环的练习1 键盘录入一个两位数的数字,表示范围 用for循环找出既能被3整除又能被5整除的数有多少个 int count=0;Scanner satisyf=new Scanner(System.in);System.out.println("请输入范围的开始:");int number=satisyf.nextInt();System.out.println(&quo…

Text-For循环的练习2

Text-For循环的练习2 用for循环求出1-100之间的偶数的和 int count=0;for(int i=1;i<=100;i++){if(i%2==0){//能被2整除的数就是偶数count+=i;}}System.out.println(count);代码运行结果如下图所示:

Text-For循环的练习3

Text-For循环的练习3 用for循环语句实现1-10的累加 int count=0;for(int i=1;i<=10;i++){count+=i;}System.out.println(count);代码运行结果如下图所示:

console.log字体背景颜色设置

console.log(%c[XX平台] -- Login Success,background: dodgerblue; color: white; padding: 4px; border-radius: 4px;, );学而不思则罔,思而不学则殆!

medusa: 牛逼密码破解工具

hw结束,十一长假,最近的股市暴涨跟暴跌,机构调整,项目减少,裁人等等发生了很多事情,变化的继续变化,不变的就是变化,回归技术研究的初心,继续研究和实战。以前曾经在研究ssh暴力破解的时候研究过medusa,最近对比测试fscan、hydra、ntscan等工具时,发现medusa可以在l…

Text-数组的练习3

Text-数组的练习3 定义一个数组,里面存入1-5 打乱他们的顺序 int arr[]={1,2,3,4,5};int i;Random r=new Random();for ( i = 0; i < arr.length; i++) {int x=r.nextInt(5);//产生随机索引与arr[i]索引换位置int temp=arr[i];arr[i]=arr[x];arr[x]=temp;}for (i = 0; i&l…

【源码】Kafka订制协议如何处理粘拆包

前言 在上一篇随笔中,我们探讨了如何使用 Netty 处理自定义协议中的粘包和拆包问题。Netty 提供了高度封装的 API,帮助开发者轻松应对这一挑战,因此很多人都对其解决方案非常熟悉。 但如果我们直接使用 Java NIO 来实现类似的功能,应该怎么做呢? Kafka,作为一个成熟的分布…