1.排列
题目链接: 原题P4678
题目描述
定义两个数组 A, B 相似,当且仅当满足以下条件:
- A, B 的长度相等,设其为 n n n。
- 对于所有 i ( 1 ≤ i ≤ n ) i(1 ≤ i ≤ n) i(1≤i≤n),A 中小于 A [ i ] A[i] A[i] 的元素个数等于 B 中小于 B [ i ] B[i] B[i] 的元素个数。
对于两个排列 P1, P2,定义 F(P1, P2) 等于满足$ P1[l · · · r] 相 似 于 P 2 相似于 P2 相似于P2[l · · · r](1 ≤ l ≤ r ≤ n)$,
并且 P 1 [ l ⋅ ⋅ ⋅ r ] P1[l · · · r] P1[l⋅⋅⋅r] 中逆序对数量小于 E 的$ (l, r)$ 的合法对数 (即合法子区间数, P i [ l ⋅ ⋅ ⋅ r ] Pi [l · · · r] Pi[l⋅⋅⋅r]
表示由$ Pi
[l], Pi
[l + 1], · · · , Pi$
[r] 组成的数组。)对$ P1, P2$ 取遍所有$ n$ 个元素的排列,求$ F(P1, P2)$ 的总和。
题意
求(任意两个长度为 N N N的全排列的贡献)之和,而两个数列中位置相等的两段区间,且满足各区间内数字大小关系相等,逆 序对个数不大于 E E E的区间贡献为 1 1 1。
分析
设 f i , j f_{i,j} fi,j表示长度为 i i i,逆序对个数为 j j j的方案数。于是可打表,得到如下的表格
1 i = 1 , k = 0 1 i = 2 , k = 0 1 1 i = 3 , k = 0 2 2 1 1 i = 4 , k = 0 3 5 6 5 3 1 1 i = 5 , k = 0 4 9 15 20 22 20 15 9 4 1 k = 10 \begin{matrix} 1_{i=1,k=0}\\1_{i=2,k=0}&1\\1_{i=3,k=0}&2&2&1\\1_{i=4,k=0}&3&5&6&5&3&1\\1_{i=5,k=0}&4&9&15&20&22&20&15&9&4&1_{k=10} \end{matrix} 1i=1,k=01i=2,k=01i=3,k=01i=4,k=01i=5,k=01234259161552032212015941k=10
看前几行会觉得可能是 f i , j = f i − 1 , j + f i , j − 1 f_{i,j}=f_{i-1,j}+f_{i,j-1} fi,j=fi−1,j+fi,j−1,但是$20+3 \not=22 $。所以只能DP了。
发现因为新插入的 i i i严格大于 1 → i − 1 1\to i-1 1→i−1,所以插入 i i i带来的新逆序对个数为 0 → i − 1 0\to i-1 0→i−1。所以有 f i , j = ∑ k = j − i + 1 j f i − 1 , k f_{i,j}=\sum_{k=j-i+1}^jf_{i-1,k} fi,j=∑k=j−i+1jfi−1,k。
然后用对每次询问,枚举区间长度,用组合式计算区间放不同数字和区间在不同位置的方案数,输出即可。
最后放一个通式
a n s = ∑ i = 1 i ≤ n [ ( C n i A n − i n − i ) 2 × c n t i , k C n − i + 1 1 ] = ∑ i = 1 i ≤ n [ ( n ! i ! ) 2 c n t i , k C n − i + 1 1 ] ans = \sum_{i=1}^{i \leq n} [ ( C_n^i A_{n-i}^{n-i} )^2 \times cnt_{i,k} C_{n-i+1}^{1} ] = \sum_{i=1}^{i \leq n} [ (\frac{n!}{i!} )^2cnt_{i,k} C_{n-i+1}^{1} ] ans=i=1∑i≤n[(CniAn−in−i)2×cnti,kCn−i+11]=i=1∑i≤n[(i!n!)2cnti,kCn−i+11]
。
代码
#include<bits/stdc++.h>
#define FOR(i,l,r) for(register int i=l;i<=r;++i)
#define ROF(i,l,r) for(register int i=l;i>=r;--i)
using namespace std;
#define int long long
const int N = 505;
const int mo = 1e9+7;
// int f[N][N*(N-1)/2],fac[N+10],invfac[N+10];
int fac[N],invfac[N],f[N][N*(N-1)/2];
//f[i][j]长度为i逆序对数为j的情况数
//可得f[i][j]=sum(f[i-1][k],k in range(1,j));
int T,n,E,PreLong,Long;
const int M = (N-1)*N/2;
inline int min(int a,int b){
return a>b?b:a;
}
inline int mod(int &a){
if(a>=mo)a%=mo;
return a;
}
int Qpow(int x,int y){
int ans = 1;mod(x);
while(y){
if(y&1)mod(ans*=x);
mod(x*=x),y>>=1;
}
return ans;
}
inline int PreWork(){//处理f[i][j]
int N = 500,M = (N-1)*N/2;
fac[0]=1;
FOR(i,1,N)fac[i]=fac[i-1]*i%mo;
invfac[N]=Qpow(fac[N],mo-2);
ROF(i,N-1,0)mod(invfac[i]=invfac[i+1]*(i+1)%mo);
FOR(i,0,M)f[1][i]=1;
FOR(i,2,N){
f[i][0]=1;Long=(i-1)*i/2;
FOR(j,1,Long){
mod(f[i][j]=f[i][j-1]+f[i-1][min(j,PreLong)]);
if(j>=i)mod(f[i][j]=f[i][j]-f[i-1][min(j-i,PreLong)]+mo);
}
PreLong=Long;
}
return 0;
}
inline int C(int n,int m){
return fac[n]%mo*invfac[m]%mo*invfac[n-m]%mo;
}
inline int PF(int x){
return x*x%mo;
}
signed main(){
PreWork();
cin>>T;
while(T--){
cin>>n>>E;int ans = 0;
E = min(n*(n-1)/2,E);
FOR(i,1,n)mod(ans+=f[i][min(E,i*(i-1)/2)]*(n-i+1)%mo*PF(C(n,i)%mo*fac[n-i]%mo)%mo);
cout<<ans%mo<<endl;
}
}
2.六边形
据说是一个状压DP,不会,这里放上标程
#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);}
inline void up(int &x,int y) {x=add(x,y);}
int A[34],B[34],top,L,k,bin[34];
int low,upi,ans=0;
int f[34][113][(1<<5)+10][2],vis[34][113][(1<<5)+10][2],vs;
int tr[(1<<5)+10][(1<<5)+10],ok[(1<<5)+10];
inline void init() {
for(int i=0;i<(1<<5);i++) {
static int len[5];
for(int z=0;z<=4;z++) len[z]=((i>>z)&1) ? 1 : (z?len[z-1]+1:1);
for(int z=0;z<=4;z++) if(len[z]>k) ok[i]=1;
for(int j=0;j<(1<<5);j++) {
static int anc[5],val[5];
for(int z=0;z<=4;z++) anc[z]=((i>>z)&1) ? z : (z-1);
for(int z=0;z<=4;z++) val[z]=((j>>z)&1) ? 1 : 0;
for(int z=1;z<=4;z++) if(!val[z] && anc[z]!=z && (z?val[z-1]:0)) {tr[i][j]=-1; break;}
if(~tr[i][j]) {
for(int z=0;z<=4;z++)
if(anc[z]==z || (val[z] && !(z?val[z-1]:0))) tr[i][j]|=(1<<z);
}
}
}
}
inline int dfs(int pos,int res,int sta,bool lim) {
if(res>=10) return 0;
if(res<-101) res=-101;
if(!pos) {
if(res>=0 || !(sta&1)) return 0;
return ok[sta] ? 0 : min(upi-low+1,-res);
}
int kth=res+101;
int &s=f[pos][kth][sta][lim];
if(vis[pos][kth][sta][lim]==vs) return s;
s=0;
for(int i=0;i<bin[5];i++) if(~tr[sta][i]) {
if(lim && ((i>>4)&1) && !B[pos]) continue;
up(s,dfs(pos-1,res*2+A[pos]-__builtin_popcount(i),tr[sta][i],lim&&((i>>4)&1)==B[pos]));
}
return vis[pos][kth][sta][lim]=vs, s;
}
inline int solve(int n) {
top=0; ++vs;
while(n) A[++top]=n&1, n>>=1;
for(int i=0;i<=max(top+1,5);i++) bin[i]=1<<i;
return dfs(top,0,0,1);
}
int main() {
freopen("hexagons.in", "r", stdin);
freopen("hexagons.out", "w", stdout);
cin>>upi>>low>>L>>k;
while(L) B[++top]=L&1, L>>=1;
init();
cout<<solve(low)<<'\n';
}