1. 程式人生 > >hdoj6129 Just do it(三種方法加詳解)

hdoj6129 Just do it(三種方法加詳解)

題目連結
方法一:正解,本質是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(n

2),說明資料相對友好,不過關於位置序列,當m是2的冪次時會較長,其他情況相對還好

#include<stdio.h>
#define P 2
int 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 200005
int 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 200005
int 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':' ');
    }
}