1034 小魂和他的数列 dp 树状数组 最长的递增序列数
作者:互联网
链接:https://ac.nowcoder.com/acm/contest/26896/1034
来源:牛客网
题目描述
一天,小魂正和一个数列玩得不亦乐乎。小魂的数列一共有n个元素,第i个数为Ai。
他发现,这个数列的一些子序列中的元素是严格递增的。
他想知道,这个数列一共有多少个长度为K的子序列是严格递增的。
请你帮帮他,答案对998244353取模。 对于100%的数据,1≤ n ≤ 500,000,2≤ K ≤ 10,1≤ Ai ≤ 109。
输入描述:
第一行包含两个整数n,K,表示数列元素的个数和子序列的长度。
第二行包含n个整数,表示小魂的数列。
输出描述:
一行一个整数,表示长度为K的严格递增子序列的个数对998244353取模的值。示例1
输入
复制5 3 2 3 3 5 1
输出
复制2
说明
两个子序列分别是2 3 3 5 1和2 3 3 5 1。
分析
dp+树状数组
题目要求最长的递增序列。
很容易想到dp
设dp[i][j]为到 i 点,长度为 j 的序列
dp[i][j] = (a[k] < a[i]) * dp[k][j-1]
但是这种做法是n^3,1e5下明显tle
观察可以知道,如果先对原数列按照从小到大的顺序排序,
遍历到i,i前面的所有k的情况都已经处理好了,那就可以直接前缀和,但是由于坐标是乱的,所以要用树状数组处理到了当前 i 坐标,长度是j ,前面 [1,i] 有多少个坐标,长度是j - 1已经被选上了
有二维考虑二维树状数组。
同时,枚举到当前i ,长度是1的位置每次循环完 k 从K 到 2 ,都要 + 1
sum(i,j) 表示长度为j,在第i个位置前面有多少满足条件的情况,累加起来即可
这里要特殊判断一下,假如数组上有两个值是一样的,如果直接前缀和如果后面的数后放进来,会导致两个一样的数被累加,但这明显不满足a[k]<a[i]。
所以如果是一样的数,排序的时候先计算位置高的数,防止计算到它。
另外长度k 要从大到小计算,如果从小到大,在同一个i 位置会滚动加上对应位置和长度的值。
//-------------------------代码---------------------------- //#define int ll const int N = 5e5+10,mod = 998244353; int n,k; struct node { int v,id; bool operator<(const node x) const { if(v == x.v) return id > x.id;//值相等,位置大的先????????? return v < x.v; } } w[N]; int a[N][11]; //a[i][j] += (a[k] < a[i]) * a[k][j-1]; //只需要把第一个数到第i个数的所有满足情况的都加起来 void add(int i,int j,int x) { for(;i<=n;i+=lowbit(i)) { a[i][j] = (a[i][j] + x) % mod;//在a[i][j] 位置 加上权值 } } int sum(int i,int j) { int res = 0; for(;i;i-=lowbit(i)) { res =(res + a[i][j]) % mod; } return res; } void solve() { // cin>>n>>m; cin>>n>>k; fo(i,1,n) cin>>w[i].v,w[i].id = i; sort(w+1,w+1+n); fo(i,1,n) { of(j,k,2) add(w[i].id,j,sum(w[i].id,j-1)); add(w[i].id,1,1);//递增序列为1的,肯定只产生一个贡献值 } cout<<sum(n,k)<<endl; } void main_init() {} signed main(){ AC();clapping();TLE; cout<<fixed<<setprecision(12); main_init(); // while(cin>>n,n) // while(cin>>n>>m,n,m) // int t;cin>>t;while(t -- ) solve(); // {solve(); } return 0; } /*样例区 */ //------------------------------------------------------------
标签:数列,int,小魂,1034,序列,长度,id,dp 来源: https://www.cnblogs.com/er007/p/16583105.html