打卡3

CodeForces - 597C Subsequences

一拿到这个题感觉很新颖,不过没啥思路。

看了大家的题解,一个$O(n^2\times k)$的做法还是能写一写,虽然过不了。

因为本题所有数字是个全排列,我们设状态是$dp[i][j]$,表示长度为$i$的子序列,以$j$结尾的方案数。

状态转移方程是:$dp[i][j]=\sum_{t=1}^{j-1}dp[i-1][t]$。预处理所有$dp[1][j]$,为了保证$t$都在$j$前面,我们可以边读入边处理。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
typedef long long ll;
ll dp[12][maxn];
int main()
{
    int n, k; scanf("%d %d", &n, &k);
    memset(dp, 0, sizeof(dp));
    for(int p = 1; p <= n; p++)
    {
        int x;
        scanf("%d", & x);
        dp[1][x] = 1;
        for(int i = 1; i <= k + 1; i++)
        {
            for(int t = 1; t <= x - 1; t++)
            {
                dp[i][x] += dp[i - 1][t];
            }
        }
    }
    ll ans = 0;
    for(int i = 1; i <= n; i++)
    {
        ans += dp[k + 1][i];
    }
    printf("%I64d\n", ans);
    return 0;
}
Code

 

这么大的复杂度显然是过不了的,那就想想怎么优化。

我们观察到求和那一项求的是前缀和,所以我们可以用树状数组来维护,复杂度降为$O(n\times k \times logn)$。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
typedef long long ll;
ll dp[12][maxn];
int n, k;
void add(int id, int x, ll val)
{
    while(x <= n)
    {
        dp[id][x] += val;
        x += (x & -x);
    }
}
ll sum(int id, int x)
{
    ll res = 0;
    while(x > 0)
    {
        res += dp[id][x];
        x -= (x & -x);
    }
    return res;
}
int main()
{
    scanf("%d %d", &n, &k);
    memset(dp, 0, sizeof(dp));
    for(int p = 1; p <= n; p++)
    {
        int x;
        scanf("%d", & x);
        add(1, x, 1);
        for(int i = 2; i <= k + 1; i++)
        {
            ll tmp = sum(i - 1, x - 1);
            add(i, x, tmp);
        }
    }
    ll ans = 0;
    ans = sum(k + 1, n);
    printf("%I64d\n", ans);
    return 0;
}
Code

 

15年CCPC有道类似题目:HDU 5542

只不过需要离散化一下,但这道题所有数字可能有重复的,不过不影响最终结果,手推一下小数据就知道了。 

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5;
typedef long long ll;
const ll mod = 1e9 + 7;
ll dp[maxn][maxn];
int maxv;
void add(int id, int x, ll val)
{
    while(x <= maxv)
    {
        dp[id][x] = (dp[id][x] + val) % mod;
        x += (x & -x);
    }
}
ll sum(int id, int x)
{
    ll res = 0;
    while(x > 0)
    {
        res = (res + dp[id][x]) % mod;;
        x -= (x & -x);
    }
    return res;
}
int a[maxn];
int dis[maxn];
int main()
{
    int T, kase = 0; scanf("%d", &T);
    while(T--)
    {
        int n, k;
        scanf("%d %d", &n, &k);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]), dis[i] = a[i];
        sort(dis + 1, dis + n + 1);
        for(int i = 1; i <= n; i++)
        {
            a[i] = lower_bound(dis + 1, dis + n + 1, a[i]) - dis;
            maxv = max(a[i], maxv);
        }
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= maxv; j++)
            {
                dp[i][j] = 0;
            }
        }
        for(int p = 1; p <= n; p++)
        {
            add(1, a[p], 1);
            for(int i = 2; i <= k; i++)
            {
                ll tmp = sum(i - 1, a[p] - 1);
                add(i, a[p], tmp);
            }
        }
        ll ans = 0;
        ans = sum(k, maxv);
        printf("Case #%d: %I64d\n", ++kase, ans);
    }
    return 0;
}
Code

 

Hills And Valleys

 暑假这道DP,当时状态方程看了别人的也很难懂,现在再拿出来搞一搞。

难受,晚上帮舍友做笔试题,好浪费时间,关键他面试不过……

明天再做完吧……

posted @ 2018-09-05 23:20  卷珠帘  阅读(161)  评论(0编辑  收藏  举报