UVA1489/POJ3986/HDU3693 Math teacher's homework
本文版权归博客园,洛谷博客和蒟蒻wjr所有,欢迎转载,但需保留此段声明,并给出原文链接,如有侵权行为,还请不吝啬向博主举报,谢谢合作。
评测了我半个小时,害的我猛然发现和上有这道题就过了
被强制要求写题解
给出两个整数, 和一个整数序列,
求满足 且的解的个数。
这题真的不好想
将移到左边得到:
显然我们就是求与异或等于的方案数
先将每个,题目就转化为:
求满足 且的解的个数。
先把这道题简单化:
有一个 个数的数列,对于任意的满足。如果,且,求满足条件的k数列的数目。
这个的答案显然是。我们先随便取个数,最后一个数显然有且只有一种取法使得条件满足。
现在我们继续增加一个条件。如果对于一个整数,数列的前项都是确定的。这时的答案是多少?
这就不用说了。如果,显然是;如果,就得看条件满足与否了。
由以上的两个分析我们可以想到这个题的一个初步做法。
(懒人盗图)
浅蓝色的部分表示和的这一位一样。绿色部分表示的这一位是,而这里是的一位,橘黄色的部分表示不可以被随便确定的位子
解释一下:
蓝色部分之所以可以随意使用是因为前面决策已经设计到这里且自己做出的决策也大于这一位(这就是我们的原因),显然根据我们刚刚给出的结论,设浅蓝色个数位,是前面的数的方案数
而橘黄色的部分之所以不能变动是因为刚扩展的位数还不清楚接下来有没有下一个数满足有这一位数,其实我们在下一个数处理就行,不影响答案。
浅绿色部分很明显是扩展到的位数
非正解(正解是这个的优化)
设表示当前决策到第个数,决策的异或值为,最低位到(同样也可以理解为到是蓝色部分)
设为的前缀(这里的前缀的意思是指二进制下从高位到低位的前若干个数组合成的数,但是不能等于)
显然根据之前给出的结论
可以得出以下转移
设为中二进制下的所在的最低位在中出现的位置的下一个出现的位置
(绕口)
(显然这么算j从,不然按照之前得出的结论,得)
解释一下这个:
- 转移到这是数位固有的转移
- 我们假设部分中的已经被确定了,显然我们必须传到下面一位是
- 显然最低肯操控的位数在绿色哪位,也就是
- 根据我们之前得出的结论,我们需要让数相乘,显然,下一个数的贡献已经被算出来,根据之前问题所得出来的结论显然是
显然数组开不下
正解
正解是上一个非正解的优化
根据打表可以发现,在和确定时,除了第位都是可以确定的
证明
根据的转移和之前j的定义,我们可以得出是所有决策前缀的最低位的在原数所出现的位置下一个出现的位置的最大值,显然每个数在算上各自j那一位都有至少匹配到这里的前缀。而不包括这一位因为各自的显然都,所以最高位到位是肯定确定的我们只需要判断第位的异或值即可
数组终于能装的下了,时间复杂度肯定过了,是
#include <cstdio>
#include <cstring>
#include <algorithm>
#define re register
#define ll long long
#define mod 1000000003
using namespace std;
template<typename T>
inline void read(T&x)
{
x=0;
char s=(char)getchar();
bool f=false;
while(!(s>='0'&&s<='9'))
{
if(s=='-')
f=true;
s=(char)getchar();
}
while(s>='0'&&s<='9')
{
x=(x<<1)+(x<<3)+s-'0';
s=(char)getchar();
}
if(f)
x=(~x)+1;
}
ll dp[55][35][2],power[35];
unsigned int a[55];
int n,k;
inline ll dfs(int pos,int val,int len)
{
val&=(~((1<<len)-1));//把0~len-1位的数清零
if(pos==n+1)
return !val;
bool flag=(val>>len)&1;
ll &ans=dp[pos][len][flag];
if(ans!=-1)
return ans;
ans=0ll;
int res=0;
for(re int i=31; i>=0; --i)
if((a[pos]>>i)&1)
{
(ans+=dfs(pos+1,val^res,max(len,i))*power[min(len,i)]%mod)%=mod;
res|=(1<<i);
// printf("%d ",res);
}
// printf("dp[%d][%d][%d][%d]=%lld\n",pos,val,len,ans);
// putchar('\n');
return ans;
}
inline void work()
{
for(re int i=1; i<=n; ++i)
{
read(a[i]);
++a[i];
}
memset(dp,-1,sizeof(dp));
printf("%lld\n",dfs(1,k,0));
}
int main()
{
power[0]=1;
for(re int i=1; i<=31; ++i)
power[i]=(power[i-1]<<1)%mod;
// for(re int i=0; i^31; ++i)
// printf("%lld\n",power[i]);
while(read(n),read(k),n||k)
work();
}
作者:蒟蒻wjr
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?