20210913测试

1.排列

题目链接: 原题P4678

题目描述

定义两个数组 A, B 相似,当且仅当满足以下条件:

  1. A, B 的长度相等,设其为 n n n
  2. 对于所有 i ( 1 ≤ i ≤ n ) i(1 ≤ i ≤ n) i(1in),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[lr] 中逆序对数量小于 E 的$ (l, r)$ 的合法对数 (即合法子区间数, P i [ l ⋅ ⋅ ⋅ r ] Pi [l · · · r] Pi[lr]
    表示由$ 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=fi1,j+fi,j1,但是$20+3 \not=22 $。所以只能DP了。

发现因为新插入的 i i i严格大于 1 → i − 1 1\to i-1 1i1,所以插入 i i i带来的新逆序对个数为 0 → i − 1 0\to i-1 0i1。所以有 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=ji+1jfi1,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=1in[(CniAnini)2×cnti,kCni+11]=i=1in[(i!n!)2cnti,kCni+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';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值