2018年10月7日提高组模拟赛 T3 排列

版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82959337

大意

现在有一个随机排列,你需要对所有的 k [ 1 , n ] k∈[1,n] ,求出长度为 k k 的上升子序列个数


思路

f [ i ] [ j ] f[i][j] 表示长度为 i i 的上升子序列以 j j 结尾的个数,得到方程

f [ i ] [ j ] = f [ i 1 ] [ k ] { j > k , a [ j ] > a [ k ] } f[i][j]=\sum f[i-1][k]\{j>k,a[j]>a[k]\}

然后这样是 O ( n 3 ) O(n^3) ,我们可以用树状数组维护前面 比它小的,有点像求逆序对,这样子时间复杂度为 O ( n 2 l o g n ) O(n^2logn) ,然后我们看到这里有一个随机排列,那么也就说明上升子序列的长度期望是 n \sqrt n 也就是说,有的时候后面子序列一旦出现长度为0的,那么所有后面的都是0

关于空间,可以用 v e c t o r vector 也可以利用期望的特点,开 n \sqrt{n} 的空间即可


代码

#include<cstdio>
#include<cstring>
#define max(a,b) (a)>(b)?(a):(b)
#define min(a,b) (a)<(b)?(a):(b)
#define ymw 1000000007
#define LL long long
using namespace std;LL n,t,a[10001],i,f[301][10001],ans,c[10001];
inline LL read()
{
	char c;int d=1,f=0;
	while(c=getchar(),c<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),c>47&&c<58)f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline void write(LL x){if(x>9)write(x/10);putchar(x%10+48);return;}
inline LL ask(LL x){LL s=0;for(;x>0;x-=x&-x)s+=c[x];return s%ymw;}//树状数组查询
inline void add(LL x,LL y){for(;x<=n;x+=x&-x)(c[x]+=y)%=ymw;return;}//树状数组修改
signed main()
{
	t=read();
	while(t--)
	{
		n=read();write(n);putchar(32);
		memset(f,0,sizeof(f));for(register int i=1;i<=n;i++)a[i]=read(),f[1][i]=1;
		ans=1;
		for(i=2;i<=n&&ans;i++)
		{
			memset(c,0,sizeof(c));
			ans=0;
			for(register int j=1;j<=n;j++)
			{
				f[i][j]=ask(a[j]-1);
				add(a[j],f[i-1][j]);
				(ans+=f[i][j])%=ymw;//动态转移
			}
			write(ans);//输出
			if(i!=n)putchar(32);
		}
		for(;i<=n;i++) {putchar(48);if(i!=n)putchar(32);}//后面的都是0啊
		putchar(10);
	}
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/82959337