题干:
给出v, k,请你找到最小的正整数n,满足:
n的二进制表示下存在一个长度为v的回文串,该回文串首尾都是1且n的二进制表示中至少有k个1。保证v,k均为偶数!
由于n可能很大,你只需要输出对取模的结果。
输入描述:
两个整数v, k。保证v,k均为偶数!
输出描述:
一个整数,表示n对取模的结果。
示例1
输入
复制
6 4
输出
复制
45
说明
样例的构造方法为:101101
示例2
输入
复制
2 4
输出
复制
15
说明
最优的构造方法为:1111
备注:
。
解题报告:
题目意思很清楚了,,不是长度为v的字符串,而是存在长度为v的子串。。。所以就直接先长度对半分,先填k,,看能不能都填上,长度为v的回文串都填满了的话那就再后面填1就行了、、、
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
const int MAX = 2e5 + 5;
const ll mod = 1e9 + 7;
int v,k;
int qq[MAX];
ll qpow(ll a,ll k) {ll res = 1;while(k) {if(k&1) res = (res*a)%mod;k>>=1;a= (a*a)%mod;}return res;
}
int main()
{cin>>v>>k;v>>=1;k>>=1;k--;qq[1]=1;for(int i = v; i>=2; i--) {if(k>0) qq[i]=1,k--;}for(int i = v+1; i<=(v<<1); i++) qq[i] = qq[v-(i-v)+1];
// for(int i = 1; i<=(v<<1); i++) printf("%d",qq[i]);if(k>0) {k<<=1;for(int i = (v<<1)+1; i<=k+(v<<1); i++) {qq[i] = 1;}}ll ans = 0;int up = k+(v<<1);
// printf("up = %d\n",up);for(int i = up; i>=1; i--) {if(qq[i] == 1) ans = (ans + qpow(2,up-i))%mod;}printf("%lld\n",ans);return 0 ;}
总结:
想的时候还是思维不够缜密啊,,这种题光满足1A是不足够的,,需要写代码的时候一次就写对,情况全都考虑到,,而且不能debug的。。这种题必须最多5分钟搞定啊。
另:其实这题写代码的时候就没想这么多,本来直接填的回文串,然后第二个样例过不了,,然后发现还有可能k>v,,所以再加点判断就过了。。。其实这种构造方法还是需要证明的,,首先如果k<=v的话那没的说就是这么填(先两头有1,然后中间往两边填1)。k>v的时候,,首先我们需要n最小所以二进制越短越好,所以我们肯定是把已有的位置充分利用一下不然浪费着这些0也是浪费着。,所以回文串的v长度肯定要填满,,其次这个肯定剩下的k还是大于0的,所以我们还需要继续填1,要么从回文串前面填1,要么后面添1,,所以肯定后面添1更小嘛(其实一样大的,,因为反正这时候回文串也是全1串了,,也就是这时候答案一定是(2^k) -1 这样的一个全1二进制数,,所以其实直接qpow一下再-1就好了嘛)
(其实这题告诉你了k个v都是偶数,,万一是那种 如果不存在就输出-1 的 就要坑死一片了、、)
(训练过程中顺便给牛客的练习赛签个到2333)