hdoj6129 Just do it(三种方法加详解)

来源:互联网 发布:安卓编程权威指南第3版 编辑:程序博客网 时间:2024/06/06 02:52

题目链接
方法一:正解,本质是dp,采用二进制优化,实现过程很巧妙(感谢Brassica)
dp[i][j]表示第i次操作后第j项的取值,根据题意可得:

dp[i][j]=dp[i][j1]dp[i1][j]=dp[i][j2]dp[i1][j1]dp[i1][j1]dp[i2][j]=dp[i][j2]dp[i2][j]//=...=dp[i][j2x]dp[i2x][j]

我们已知dp[0][1~n],求dp[m][1~n]
将m展开为2进制的形式,从低位到高位,为1的位的对应序号即为公式中x的值,需要进行一次更新的操作。
这部分靠意会233,结合代码慢慢理解吧(ง •_•)ง
复杂度O(nlog(m))

#include<stdio.h>int a[200005],t,n,m,i,j;int main(){    int t;    scanf("%d",&t);    while(t--){        scanf("%d%d",&n,&m);        for(i=1;i<=n;i++) scanf("%d",a+i);        for(j=1;m;m>>=1,j<<=1)if(m&1)            for(i=j+1;i<=n;i++) a[i]^=a[i-j];        for(i=1;i<=n;i++) printf("%d%c",a[i],i==n?'\n':' ');    }}

方法二:赛场上鄙队采用的做法,lucas判组合数奇偶+平移
首先,当我们知道a[1]在m次操作之后将要在哪些下标位置做异或运算之后,a[2]~a[n]即为该位置序列分别向右平移1~n-1位
那么下面只考虑a[1]在m次操作中的变换:
异或两次会消掉,但是现在我们不消掉,而是考虑异或次数之和,打表之后就会看到亲切的杨辉三角(斜着的),这就好办了,用组合数可以直接得到第m行的情况辣
比赛时我来不及思考太多只想赶快把这题给过掉就上了lucas,模2判奇偶(没想到官方题解1010中也提到了用lucas判组合数奇偶,hin开森)
复杂度…好吧我承认是O(n2),说明数据相对友好,不过关于位置序列,当m是2的幂次时会较长,其他情况相对还好

#include<stdio.h>#define P 2int f[P],r[P];int C(int n,int m){ return n<m?0:f[n]*r[n-m]%P*r[m]%P; }int lucas(int n,int m){    if(n<m) return 0;    if(!m||n==m) return 1;    return C(n%P,m%P)*lucas(n/P,m/P)%P;}#define N 200005int a[N],b[N],s[N];int main(){    int t,n,m,i,j,k,tmp;    for(f[0]=f[1]=r[0]=r[1]=1,i=2;i<P;i++){        f[i]=f[i-1]*i%P,r[i]=-r[P%i]*(P/i)%P;        if(r[i]<0) r[i]+=P;    }    for(i=2;i<P;i++) r[i]=r[i]*r[i-1]%P;    scanf("%d",&t);    while(t--){        scanf("%d%d",&n,&m),m--;        for(i=1;i<=n;i++) scanf("%d",&a[i]),s[i]=0;        for(k=i=0;i<n;i++){    //i表示位置序列从下标出发需要平移的距离,故从0开始            tmp=lucas(m+i,i);            if(tmp&1) b[++k]=i;        }        for(i=1;i<=n;i++)for(j=1;j<=k;j++){            if(i+b[j]>n) break;            s[i+b[j]]^=a[i];        }        for(i=1;i<=n;i++) printf("%d%c",s[i],i==n?'\n':' ');    }}

方法三:
感谢zyyyyy大佬告诉我了一个厉害的性质,用这个可以替换掉方法二中lucas判组合数奇偶的部分,代码更简洁,跑得更快

c[n][m]{,,if n&m==motherwise

#include<stdio.h>#define N 200005int a[N],b[N],s[N];int main(){    int t,n,m,i,j,k,tmp;    scanf("%d",&t);    while(t--){        scanf("%d%d",&n,&m),m--;        for(i=1;i<=n;i++) scanf("%d",&a[i]),s[i]=0;        for(k=i=0;i<n;i++) if(((m+i)&i)==i) b[++k]=i;        for(i=1;i<=n;i++)for(j=1;j<=k;j++){            if(i+b[j]>n) break;            s[i+b[j]]^=a[i];        }        for(i=1;i<=n;i++) printf("%d%c",s[i],i==n?'\n':' ');    }}
原创粉丝点击