牛客练习赛56 小魂和他的数列

小魂和他的数列

题目链接

题目大意

给出一个数列,让求长度为k的严格递增子序列有多少个

怎么做呢?
显然dp 这个是很好想的

for (int i=  1; i <= n; i ++ )
	{
		dp[i][1] =1;
	}
	for (int i = 2; i <= m; i ++ )
	{
		for(int j = i; j <= n; j ++ )
		{
			for (int  k = 1; k < j; k ++ )
			{
				if(a[k] < a[j])
				{
					dp[j][i] += dp[k][i-1];
				}
			}
		}
	}

这个dp显然tle 所以 当时没有做出来。 做到这里不会做了。。
想不到怎么优化。
显然在最里面的那个循环的作用是前缀和 (小于a[i]的位置的dp的前缀和)
于是你得想到树状数组优化。。 我太菜了想不到
开k个树状数组优化最里面的那一层循环
于是就得到先离散化再根据他的值加进去 再求前缀和加到dp里的操作
于是就写了个这个:

	for (int i = 1; i <= n; i ++ )
	{
		vis[i] = lower_bound(vv.begin(),vv.end(),a[i]) - vv.begin() + 1;
	}
	l = vv.size();
	for (int i=  1; i <= n; i ++ )
	{
		dp[i][1] =1;
	}
	for(int i = 2; i <= m; i ++ )
	{
		for (int j = 1; j <= n; j ++ )
		{
			add(vis[j],i - 1,dp[j][i-1]);
			dp[j][i] = (dp[j][i] + query(vis[j] - 1,i - 1))%mod;
		}
	}

本来只能过60% 加个vis数组预处理了一下离散化后的值
能过85%
可是这又不是他妈的oi赛制
还是a不了
于是参考了大佬博客了。。。
地址
代码:

#include<stdio.h>
#include<vector> 
#include<algorithm>
using namespace std;
const int mod = 998244353;
const int maxn = 5e5+5;
int a[maxn];
int dp[maxn][11];
int tree[maxn][11];
int l;
int lowbit(int x)
{
	return x & (-x);
}
void add(int x,int f,int num)
{
	while( x <= l )
	{
		tree[x][f] = (tree[x][f] + num) % mod;
		x += lowbit(x);
	}
}
int query(int x,int f)
{
	int ans = 0;
	while(x > 0)
	{
		ans =  (ans + tree[x][f])%mod;
		x -= lowbit(x);
	}
	return ans;
}
vector<int> vv;
int num[maxn];
int vis[maxn];
int temp[maxn];
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i ++ )
	{
		scanf("%d",&a[i]);
		vv.push_back(a[i]);
	}
	sort(vv.begin(),vv.end());
	vv.erase(unique(vv.begin(),vv.end()),vv.end());
	for (int i = 1; i <= n; i ++ )
	{
		vis[i] = lower_bound(vv.begin(),vv.end(),a[i]) - vv.begin() + 1;
	}
	l = vv.size();``
	for (int i = 1; i <= n; i ++ )
	{
		num[vis[i]] ++ ;
		dp[vis[i]][1] = num[vis[i]];
		add(vis[i],1,1);
		for (int j = 2; j <= m; j ++ )
		{
			temp[j] = query(vis[i] - 1,j - 1);
			dp[vis[i]][j] = (dp[vis[i]][j] + temp[j])%mod;
		}
		for (int j = 2; j <= m; j ++ )
		{
			add(vis[i],j,temp[j]);
		}
	}
	int ans = 0;
	for (int i =1; i <= n ; i++ )
	{
		ans =(ans + dp[i][m]) %mod;
	}
	printf("%d\n",ans%mod);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值