「CodeM」排列

传送门

Description

(n) 个二维点 ((a_i,b_i)),询问有多少种排列 (p)(答案对 (10^9+7) 取模)使得执行以下伪代码后留下的点是 (i),即最后 ( ext{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)<<"
"
#define dbg3(x) cerr<<#x<<"
"
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
",Mul(fac,ans[i]));
	return 0;
} 


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

原文地址:https://www.cnblogs.com/PaperCloud/p/11662829.html