• Pinely Round 2 (Div. 1 + Div. 2) F. Divide, XOR, and Conquer(区间dp)


    题目

    给定长为n(n<=1e4)的数组,第i个数为ai(0<=ai<2的60次方)

    初始时,区间为[1,n],也即l=1,r=n,

    你可以在[l,r)中指定一个k,将区间分成左半边[l,k]、右半边[k+1,r]

    1. 如果左半边异或和与异或和的异或和相等,则可以二选一,要么保留左半边,要么保留右半边

    2. 否则,只能保留异或和大的那半边

    当l=r时,游戏结束

    对于每个i,判断是否能通过适当操作,使得游戏结束时l=r=i

    实际t(t<=1e4)组样例,保证sumn不超过1e4

    思路来源

    力扣群 潼神

    题解

    这个st0[i]和ed0[i]实际只需要占一位,分开写的话可读性会好一点

    此处由于值域限制,直接维护在了st[i]和ed[i]的第60位

    n=1e4,说明只能是O(1)转移的区间dp

    异或和的两种情况:

    1. [l,r]异或和为0,那么[l,x](xl)的区间都可以异或出

    2. [l,r]异或和为s(s≠0),记s的最高位为b,

    那么,如果[l,x](x

    同理,如果[y,r](y>l)的异或和包含b这一位,[y,r]的异或和就一定大于[l,y-1]的异或和

    判断

    ①左端点/右端点第60位打过标记,说明存在共左端点/右端点的更大的区间异或和为0

    ②[l,r]异或和为s,s和左端点/右端点的标记有交,说明存在共左端点/右端点的更大的区间的异或和的最高位能被s取到,也就是s比区间另一半大

    设位

    ①如果异或和为0,在第60位打标记

    ②否则,在异或和最高位打标记

    心得

    本题是长区间向短区间下放,没怎么写过,但本身区间dp也很灵活

    由于下放时一定需要固定一个端点,所以可以将信息维护在端点处供后续使用

    也就只需要开一维,不像传统区间dp开两位数组那样了

    __builtin_clzll(s)是获取64位数二进制前导0个数

    63-__builtin_clzll(s)是获取64位数二进制最高位的1是第几位

    从右往左,从第0位开始数,也就是1<

    32位数时,可以对应改成__builtin_clz(s)、31-__builtin_clz(s)

    代码

    1. #include
    2. using namespace std;
    3. #define rep(i,a,b) for(int i=(a);i<=(b);++i)
    4. #define per(i,a,b) for(int i=(a);i>=(b);--i)
    5. typedef long long ll;
    6. typedef double db;
    7. typedef pairint> P;
    8. #define fi first
    9. #define se second
    10. #define pb push_back
    11. #define dbg(x) cerr<<(#x)<<":"<" ";
    12. #define dbg2(x) cerr<<(#x)<<":"<
    13. #define SZ(a) (int)(a.size())
    14. #define sci(a) scanf("%d",&(a))
    15. #define pt(a) printf("%d",a);
    16. #define pte(a) printf("%d\n",a)
    17. #define ptlle(a) printf("%lld\n",a)
    18. #define debug(...) fprintf(stderr, __VA_ARGS__)
    19. typedef unsigned ui;
    20. //typedef __uint128_t L;
    21. typedef unsigned long long L;
    22. typedef unsigned long long ull;
    23. const int N=1e4+10,B=60;//xor=0代表的位
    24. int t,n;
    25. ll v,bl[N],br[N],sum[N];
    26. char ans[N];
    27. bool cal(int l,int r){
    28. if(l==1 && r==n)return 1;
    29. //之前的[l,R](R>r)的异或和有0
    30. //之前的[L,r](L
    31. if(bl[l]>>B&1 || br[r]>>B&1)return 1;
    32. ll s=sum[r]^sum[l-1];
    33. return (s&bl[l]) || (s&br[r]);
    34. //[l,r]的异或和有[l,R](R>r)的异或和的最高位
    35. //[l,r]的异或和有[L,r](L
    36. }
    37. void op(int l,int r){
    38. ll s=sum[r]^sum[l-1];
    39. int b;
    40. if(!s)b=B; // 当前[l,r]的异或和有0
    41. else b=63-__builtin_clzll(s); // 当前[l,r]的异或和的最高位
    42. bl[l]|=1ll<
    43. br[r]|=1ll<
    44. }
    45. int main(){
    46. sci(t);
    47. while(t--){
    48. sci(n);
    49. rep(i,1,n){
    50. scanf("%lld",&v);
    51. sum[i]=sum[i-1]^v;
    52. bl[i]=br[i]=0;
    53. ans[i]='0';
    54. }
    55. per(sz,n,1){
    56. rep(l,1,n+1-sz){
    57. int r=l+sz-1;
    58. //printf("l:%d r:%d ok:%1d s:%lld b:%d\n",l,r,cal(l,r),sum[r]^sum[l-1],63-__builtin_clzll(sum[r]^sum[l-1]));
    59. if(cal(l,r)){
    60. op(l,r);
    61. if(l==r)ans[l]='1';
    62. }
    63. }
    64. }
    65. ans[n+1]='\0';
    66. printf("%s\n",ans+1);
    67. }
    68. return 0;
    69. }

  • 相关阅读:
    前端html原生页面兼容多端H5和移动端适配方案
    一分钟!图片生成32种动画;Adobe绘画工具大升级;复盘Kaggle首场LLM比赛;VR科普万字长文 | ShowMeAI日报
    《算法导论》11.3 除法散列法、乘法散列法 11.4 开放寻址法
    yarn包管理工具
    搭建zerotier planet服务
    web前端开发--------CSS基础教程
    Java面向对象(高级)-- 单例(Singleton)设计模式
    python随手小练4
    React Native
    十分钟轻松入门 nw.js 实现桌面应用程序
  • 原文地址:https://blog.csdn.net/Code92007/article/details/132660891