题解 UVA12983 【The Battle of Chibi】

题面

https://www.luogu.com.cn/problem/UVA12983

题意翻译

本题题意就是让你码个程序,然后这个程序是要你在一个数字序列中找到长度为m的严格上升子序列的个数(注意不是子串),然后答案对 10^9 + 7109+7 取模。

什么意思呢?举个例子(当然不是题目中的例子):

首先是n=5,m=3。 然后是一个数字序列: 1 3 4 2 5

那么符合条件的序列就有: 1 3 4 1 2 5 1 3 5 1 4 5 3 4 5

于是最终答案就是5 。 这样题意应该就比较清晰了
数据范围:1<=M<=N<=1000;1<=ai<=1e9;1<=T<=100;mod=1e9+7;

简介

来一发简单易懂的题解,也是本蒟蒻的第一篇题解。首先这道题,很明显就是dp。

用dp[i][j]表示以第i个数结尾,长度为j的严格递增序列的长度。

很容易可以得出

dp[i][j]=sum{dp[k][j-1]}1<=k<=i-1,且a[k]<a[i]

但是考虑到一个个找效率不高,于是考虑用树状数组优化,记树状数组为

tree[i][j],表示长度为i且末尾分别的值为j-( j & ( -j ) )+1 ~ j 的严格递增序列的个数之和

其中(j&(-j))表示二进制数最右边的1所表示的数值

即 6(10)=110(2),那么6&(-6)=10(2)=2

编完之后可以发现,dp[ i ][ j ]其实是无用的(代码中有解释)。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int MOD=1e9+7;
int n,m,tree[1001][1001],pos;
struct date {
    int num;//原序列中的位置
    int val;//原数值
    int big;//重新赋值后的数值
} a[3005];
bool cmp1(date x,date y) {
    return x.val<y.val;//按原数值从小到大排序
}
bool cmp2(date x,date y) {
    return x.num<y.num;//排回原数列的位置.
}
void change(int *z,int x,int y) {
    while(x<=n) {
        z[x]+=y;
        z[x]%=MOD;//记得模,我因为没mod错了好几次
        x+=(x&(-x));
    }
}//改变值
int sum(int *z,int x) {
    int ans=0;
    while(x) {
        ans+=z[x];
        ans%=MOD;
        x^=(x&(-x));
    }
    return ans%MOD;
}//求dp[1][k]~dp[x][k]之和.
void solve() {
  //memset(dp,-127/3,dp);
    memset(tree,0,sizeof(tree));
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)scanf("%d",&a[i].val),a[i].num=i;
    sort(a+1,a+1+n,cmp1);
    a[1].big=1;
    int j=1;
    for(int i=2; i<=n; i++) {
        if(a[i-1].val!=a[i].val)j++;
        a[i].big=j;
    }//重新赋值,不然数组开不了那么大.
    sort(a+1,a+1+n,cmp2);
    for(int i=1; i<=n; i++) {
    //dp[i][1]=1;
        change(tree[1],a[i].big,1);//第一个做特殊处理
        for(int j=m; j>1; j--) {
            int k=sum(tree[j-1],a[i].big-1);
            if(k) {
          //dp[i][j]=k;
                change(tree[j],a[i].big,k);
            }//如果k非0说明前面存在j-1长度的,则转移.
        }
    }
    pos++;//计数
    printf("Case #%d:  %d\n",pos,sum(tree[m],n)%MOD);
   //看到这里大家应该能发现实际我们存的dp数组没啥用,所以
   //可以省略掉
}
int main() {
    int t;
    scanf("%d",&t);
    while(t--) {
        solve();
    }
    return 0;
}

后言

有个小小的反抄袭操作,希望大家能自己理解一下,诚信做题,对树状数组不太了解的同学可以先去看看树状数组模板题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值