zoukankan      html  css  js  c++  java
  • 【(好题)组合数+Lucas定理+公式递推(lowbit+滚动数组)+打表找规律】2017多校训练七 HDU 6129 Just do it

    http://acm.hdu.edu.cn/showproblem.php?pid=6129

    【题意】

    • 对于一个长度为n的序列a,我们可以计算b[i]=a1^a2^......^ai,这样得到序列b
    • 重复这样的操作m次,每次都是从上次求出的序列a得到一个新序列b
    • 给定初始的序列,求重复m次操作后得到的序列

    【方法一】

    假定n=5,我们模拟一次可以发现,经过m次操作后a1在b1......bn中出现的次数为:

    • m=0: 1 0 0 0 0
    • m=2: 1 2 3 4 5
    • m=3: 1 3 6 10 15
    • m=4:1 4 10 20 35
    • m=5:1 5 15 35 70

    (画个杨辉三角出来就可以看出这些都是组合数)

    容易推出来操作m次后,a1在答案bi中出现的次数C(m+i-2,m-1),由于是异或,我们只需知道C(m+i-2,m-1)%2

    根据Lucas定理(2是素数),x=m-1,y=m+i-2,C(x,y)%2为x和y写成二进制后每一位C(xi,yi)%2的连乘,可以发现,只有C(0,1)是0,也就是C(x,y)%2==0  <=> y的二进制位置为1的集合是x的子集 <=> (x&y)==y

    这道题m=1的时候是n^2的,然而这个复杂度是可以过的(奇怪)

    附上AC代码

    【AC】

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int maxn=2e5+2;
     5 ll a[maxn];
     6 ll b[maxn];
     7 int n;
     8 ll m;
     9 int main()
    10 {
    11     int T;
    12     scanf("%d",&T);
    13     while(T--)
    14     {
    15         memset(b,0,sizeof(b));
    16         scanf("%d%I64d",&n,&m);
    17         for(int i=1;i<=n;i++)
    18         {
    19             scanf("%I64d",&a[i]);
    20         }
    21         for(int i=1;i<=n;i++)
    22         {
    23             ll y=m-1;
    24             ll x=m+i-2;
    25             if((x&y)==y)
    26             {
    27                 for(int j=i;j<=n;j++)
    28                 {
    29                     b[j]^=a[j-i+1];
    30                 }
    31             }
    32         }
    33         for(int i=1;i<=n;i++)
    34         {
    35             if(i==1) printf("%I64d",b[i]);
    36             else printf(" %I64d",b[i]);
    37         }
    38         puts("");
    39     }
    40     return 0;
    41 }
    View Code

    【经验】

    • 把a,b二进制表达后b中1的位置是a中1的位置的子集    <=> (a&b)==b
    • C(2^k-1,i)%2==1                     <=>               0<=i<=2^k-1

    ******************************************************************************************************************************** 

    【方法二】

    dp(i, j)表示第i次变换第j列的数。 
    dp(i, j) = dp(i, j-1)^dp(i-1, j) = dp(i, j-2)^dp(i-1, j-1)^dp(i-1, j-1)^dp(i-2, j) = dp(i, j-2)^dp(i-2, j) => dp(i, j-2^n)^dp(i-2^n, j) 
    从m中的最高位或最低位开始递推。

    类似于dp[i][j]=dp[i-1][j]+dp[i][j-1],最后求dp[m][j](1<=j<=n)我们可以开一个滚动数组从前往后递推(dp[i][j]=dp[i-1][j]+dp[i][j+1]则是从后往前推)

    1 for(int i=1;i<=m;i++)
    2 {
    3     for(int j=1;j<=n;j++)
    4     {
    5         dp[j]^=dp[j-1];
    6     }
    7 }
    dp[i][j]=dp[i-1][j]^dp[i][j-1]

    计算dp[i][j]=dp[i-2^n][j]^dp[i][j-2^n],用lowbit从右往左枚举m的每一位,每一次都对整列进行更新。

    1 while(m)
    2 {
    3     int x=lowbit(m);
    4     for(int j=x;j<=n;j++)
    5     {
    6         dp[j]^=dp[j-x];
    7     }
    8     m-=x;
    9 }
    dp[i][j]=dp[i-2^n]^dp[i][j-2^n]

    【AC】

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int maxn=2e5+3;
     5 int n,m;
     6 int dp[maxn];
     7 int main()
     8 {
     9     int T;
    10     scanf("%d",&T);
    11     while(T--)
    12     {
    13         scanf("%d%d",&n,&m);
    14         for(int i=0;i<n;i++)
    15         {
    16             scanf("%d",&dp[i]);
    17         }
    18         while(m)
    19         {
    20             int x=m&(-m);
    21             for(int i=x;i<n;i++)
    22             {
    23                 dp[i]^=dp[i-x];
    24             }
    25             m-=x;
    26         }
    27         for(int i=0;i<n;i++)
    28         {
    29             if(i==0) printf("%d",dp[i]);
    30             else printf(" %d",dp[i]);
    31         }
    32         puts("");
    33     }
    34     return 0;
    35 }
    递推+lowbit+滚动数组

    ***********************************************************************************************************************************

     【方法三】

    http://m.blog.csdn.net/silence255713/article/details/77200056

  • 相关阅读:
    获取ocx运行路径的另一种方法
    使用D3D渲染YUV视频数据
    C++(MFC)中WebBrowser去除3D边框的方法(实现IDocHostUIHandler接口)
    ActiveX控件的安全初始化和脚本操作 和 数字签名SIGN
    解决Eclipse中的卡死现象
    Http请求头和响应头
    HTTP请求头与响应头
    centos7 Mariadb5.5升级到Mariadb10.2
    window下利用navicat访问Linux下的mariadb数据库
    在Linux上安装及配置MariaDB
  • 原文地址:https://www.cnblogs.com/itcsl/p/7375198.html
Copyright © 2011-2022 走看看