hdoj6129 Just do it(三種方法加詳解)
阿新 • • 發佈:2019-02-16
題目連結
方法一:正解,本質是dp,採用二進位制優化,實現過程很巧妙(感謝Brassica)
dp[i][j]表示第i次操作後第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( 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判組合數奇偶的部分,程式碼更簡潔,跑得更快
#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':' ');
}
}