「CodeM」排列

传送门

Description

\(n\) 个二维点 \((a_i,b_i)\),询问有多少种排列 \(p\)(答案对 \(10^9+7\) 取模)使得执行以下伪代码后留下的点是 \(i\),即最后 \(\text{saved} = i\)

saved = p[1]
for x from 2 to n
    if a[p[x]] >= a[saved] and b[p[x]] >= b[saved]
        saved = p[x]

保证 \(a\)\(b\) 分别为一个排列。

Solution 

对于一个点,如果不存在两维都比他大的点,那么它有解

考虑\(saved\)总共存储过的值,设为\(A_1,...,A_k\)

那么,在所有横纵坐标都比\(A_i\)的大的点中,\(A_{i+1}\)一定是最早出现的

\(num[i]\)表示有\(num[i]\)个点一定不能排在\(i\)之前(\(i\)自己也算),特别的\(num[p_1]=n\)

所以这样的排列(指依次停留在\(A_1,...,A_k\))会有\(\frac{n!}{\prod_{i=1}^k num[A_i]}\)

简化为\(\frac{(n-1)!}{\prod_{i=1}^{k-1}b[A_i]}\)\(b[i]\)表示横纵坐标都比\(i\)号点大的点的个数

可以\(dp\)求解,需要做个二维求和,可以先对一维排序,第二维用树状数组维护即可

Code 

#include<bits/stdc++.h>
#define ll long long
#define dbg1(x) cerr<<#x<<"="<<(x)<<" "
#define dbg2(x) cerr<<#x<<"="<<(x)<<"\n"
#define dbg3(x) cerr<<#x<<"\n"
using namespace std;
#define reg register
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
const int MN=1e5+5,P=1e9+7;
int Mul(int x,int y){return (1ll*x*y)%P;}
int Add(int x,int y){return (x+y)%P;}
int N,inv[MN];
struct d{int x,y,id;}a[MN];
int T[MN],b[MN],ans[MN];
void C(int x,int y){for(;x<=N;x+=(x&-x))T[x]=Add(T[x],y);}
int G(int x){int r=0;for(;x;x-=(x&-x))r=Add(r,T[x]);return r;}
bool cmp(d x,d y){return (x.x!=y.x)?(x.x<y.x):(x.y<y.y);}
int main()
{
	N=read();reg int i;
	for(inv[0]=inv[1]=1,i=2;i<=N;++i)inv[i]=Mul(inv[P%i],(P-P/i));
	for(i=1;i<=N;++i)a[i].x=read(),a[i].y=read(),a[i].id=i;
	std::sort(a+1,a+N+1,cmp);
	for(i=N;i;--i)b[i]=N-i-G(a[i].y),C(a[i].y,1);
	memset(T,0,sizeof T);C(1,1);
	for(i=1;i<=N;++i)
	{
		int V=G(a[i].y);
		if(b[i])C(a[i].y,Mul(V,inv[b[i]]));
		else ans[a[i].id]=V;
	}
	int fac=1;for(i=1;i<N;++i)fac=Mul(fac,i);
	for(i=1;i<=N;++i)printf("%d\n",Mul(fac,ans[i]));
	return 0;
} 


Blog来自PaperCloud,未经允许,请勿转载,TKS!

posted @ 2019-10-12 17:22  PaperCloud  阅读(185)  评论(0编辑  收藏  举报