【以前的空间】bzoj 1072 [SCOI2007]排列perm
又颓废了一个下午,最近撸mc撸到丧失意识了,玩的有点恶心,于是找水题做,瞧不起颓废的自己啊。
another水题。
这题题意很明显啦,就是找数字排列后组成的数去mod d=0后有多少种。
普通的搜索的话,是会tle的(应该是o(n!)没错?)。注意到长度n还是比较小的,于是想到状压dp。
状态就是每个数取和不取组成的结果(就是00110表示第3,4个数取了啦,学过状压都知道)。
然后转移就是f[i,j,k]表示现在取到第i个数状态为i余数为j有多少种情况,
那么f[i,j,(k*10+a[i])mod d]=Σf[i-1,j',k](也就是同余的东西啦,在123后加一个数字4,那么1234 mod d=((123 mod d *10)+4 )mod d )。k的范围就是0-(d-1)啦。其实就是一个01背包,然后i是可以去掉的。
最后就是处理重复的问题了,之前好像也做过类似的,不过反过来的……重复的话可以这么想,比如有4个2重复,那么对于第4个2来说,加上它后重复的情况就是之前的情况*4,对于第三个2来说,加上它后重复的情况就是之前的情况*3,对于第二个2就是之前的情况*2(其实就是个排列组合……四个2有序号要比没有序号多A(4,4)=4!)。
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 var b:array[0..10,0..2000]of longint; c,a:array[0..20]of longint; f:array[0..2000,0..1500]of longint; i,j,k,l,n,m,t,tot,top:longint; procedure into; var i,j,k,len:longint; s:string; begin readln(s); len:=length(s); i:=pos(' ',s); val(copy(s,i+1,len-i),tot); delete(s,i,len-i+1); n:=length(s); fillchar(c,sizeof(c),0); for i:=1 to n do begin a[i]:=ord(s[i])-ord('0'); inc(c[a[i]]); end; top:=1<<n-1; fillchar(b,sizeof(b),0); for i:=0 to top do begin j:=i; k:=0; while j<>0 do begin inc(k); dec(j,j and (-j)); end; j:=k; inc(b[j,0]); b[j,b[j,0]]:=i; end; end; procedure work; var i,j,k,l,m,ans:longint; begin fillchar(f,sizeof(f),0); for i:=1 to n do f[1<<(i-1),a[i] mod tot]:=1; for i:=2 to n do for j:=1 to b[i,0] do begin k:=b[i,j]; for l:=1 to n do if k and (1<<(l-1)) <> 0 then for m:=0 to tot-1 do inc(f[k,(10*m+a[l]) mod tot],f[k-1<<(l-1),m]); end; ans:=f[top,0]; for i:=0 to 9 do if c[i]>1 then for j:=2 to c[i] do ans:=ans div j; writeln(ans); end; begin readln(t); while t>0 do begin dec(t); into; work; end; end.
因疲惫而轻易入眠,是对自己一天努力的最好褒奖。 要和泪子一起努力怀抱梦想对抗残酷的现实……