XJOI NOIP模拟题2
第一题
组合计数
分析:
从前往后一位一位的计算
先算第一位比t小的数目,再算第一位与t[1]相同,第2位比t小的个数以此类推
先预处理一个数组h,h[i]表示从1~it串与s串不同的位数
对于第i位,
由于1~i-1与s串一样,故i~n中与s串不同的个数有k-h[i-1]个,设v=k-h[i-1]。
F(i)表示’a'到t[i]中与s[i]不相同字母个数(不包括t[i]),
当s[i]>=t[i]则
第i位开始比t小的数目ANS[i]=F(i)*C(n-i,v-1)*(25)^(v-1)
当s[i]<t[i]则
第i位开始比t小的数目ANS[i]=[F(i)-1]*C(n-i,v-1)*(25)^(v-1) +C(n-i,v)*25^v
输出∑ANS[i]+1即可。
代码:
program t1; const num=1000000007; var a,b:array[0..100001]of char; d,h,f,g:array[0..100001]of int64; n,m,v,len,x,y,ans:int64; i:longint; function get(x,y:int64):int64; var i:longint; t:int64; begin if (x=0)or(y=0)or(x=y) then exit(1); exit(((f[x]*g[x-y]) mod num)*g[y] mod num); end; function quick(x,y:int64):int64; var ans:int64; begin ans:=1; while y>0 do begin if y and 1>0 then ans:=ans*x mod num; x:=x*x mod num; y:=y>>1; end; exit(ans); end; procedure make; var i:longint; begin f[0]:=1; g[0]:=1; for i:=1 to n do begin f[i]:=(f[i-1]*i) mod num; g[i]:=g[i-1]*quick(i,num-2) mod num; end; end; begin readln(n,m); d[0]:=1; ans:=0; for i:=1 to m do d[i]:=(d[i-1]*25) mod num; for i:=1 to n do read(a[i]); readln; for i:=1 to n do read(b[i]); for i:=1 to n do if a[i]<>b[i] then h[i]:=h[i-1]+1 else h[i]:=h[i-1]; for i:=n downto 1 do h[i]:=m-h[i-1]; make; for i:=1 to n do begin if a[i]<b[i] then len:=ord(b[i])-ord('a')-1 else len:=ord(b[i])-ord('a'); v:=h[i]; if v=0 then break; y:=get(n-i,v-1); ans:=ans+((d[v-1]*y)mod num*len) mod num; if (a[i]<b[i])and(n-i>=v) then begin x:=get(n-i,v); ans:=(ans+d[v]*x) mod num; end; end; writeln((ans+1)mod num); end.
第二题
第三题
树型DP
分析:
最容易想到的O(n*m*m)的DP,用f[i,j]表示以i为根子树选j个的最大价值,f[i,j]=∑f[k,w[k]] w[k]表示k节点选取的点数,显然这是一个背包
然而这样的效率是不够的。
可以改变思路