HDU 5694 BD String 二分或递归
题目链接:HDOJ http://acm.hdu.edu.cn/showproblem.php?pid=5694
VJ https://vjudge.net/problem/HDU-5694
方法一 : 分类讨论:
对于任一length>=1的BD串可以分两类:A类满足中点为B,B类满足中点为D
由进行两组操作后结果可知:同一个串的子A,B子串在除再进行二分的对称点外的其他部分相同,所以该题可以二分求解。
二分法代码如下:
代码使用JAVA,和C++基本没区别就不另给c++版本了
import java.util.Scanner; public class Main { static long a[]=new long[61]; static long b(long x) { long ans=0; int t=2;//t=1为B类,t=2为A类,易知开始递归前所有串均为A类 while(x!=0) { for(int i=1;i<=60;i++) { if(a[i]==x) return ans+a[i-1]+1;//刚好剩下一个完整的A,或B类串 else if(a[i]+1==x)return ans+a[i-1]+t;//剩下一个完整的A,或B类串和对称轴
else if(a[i]>x) {//取走A串+对称轴部分,递归剩余部分
ans+=a[i-2]+t;x=x-a[i-1]-1;
/* 改变字符串的A,B性 */ if(x>a[i-2]) { t=1; } else { t=2; } break; } } } return ans; } public static void main(String[] args) { // TODO Auto-generated method stub Scanner in=new Scanner(System.in);
/*初始化a数组,a[i]表示S(i)的长度,A类:a[i-1]+1,B类:a[i-1]表示S(i)中B的个数*/
a[0]=0; for(int i=1;i<=60;i++) { a[i]=a[i-1]*2+1; } int t=in.nextInt(); while(t-->0)System.out.println(b(in.nextLong()-1)*-1+b(in.nextLong())); } }
方法2:递归
思路:可以用一个函数calc(x),表示从第一位到第x位中B字符的个数,则结果为calc(R)-calc(L),怎么样求calc(x)呢?
用一个数组a[1]~a[60](a[60]>10^18&a[59]<10^18)表示s(1)~s(60)的串长,若x==a[i],则sum=a[i]/2+1;若x<a[i]&x>a[i-1],则对应a[i-1]+1的字符一定为B,那么关于a[i-1]+1对称的两部分a[i-1]+2~x与a[i]-R+1~a[i-1]字符串取反后对称,所以这两部分字符转中B的个数和为R-a[i-1]-1,加上中间对称的B,个数为R-a[i-1],总的B字符个数为R-a[i-1]+calc(a[i]-R),然后递归求解calc(a[i]-R);
代码如下:
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cmath> using namespace std; long long a[70]; long long calc(long long x) { if(x==0) return 0; long long sum=0; for(int i=1;i<=60;i++) { if(a[i]==x) return x/2+1; if(x<a[i]) { sum+=x-a[i-1]; sum+=calc(a[i]-x); break; } } return sum; } int main() { int T; long long L,R; scanf("%d",&T); a[0]=0; for(int i=1;i<=60;i++) { a[i]=a[i-1]*2+1; } while(T--) { scanf("%I64d%I64d",&L,&R); printf("%I64d\n",calc(R)-calc(L-1)); } return 0; }