51nod 1218 最长递增子序列 V2——LIS+思路(套路)

本文详细解析了51NOD平台上的题目1218,分享了解题思路与代码实现过程,强调了深入分析与避免匆忙的重要性。

题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1218

自己怎么连这种 喜闻乐见的大水题 都做不出来了……

好像见过的套路,就是求每个位置到它为止的LIS和从它开始的LIS,最后拼起来是ans+1的就在LIS上。

然后试图通过方案数来判断经过该位置的LIS有多少,以判断该位置是不是唯一的。

WA了一次后发现自己的树状数组传参没有-1,求成非严格的了。

还是WA了后面的点。给方案数开了long long后多A了几个点,但还是不能AC。尝试取模,通过的点数和没开long long一样。

然后去看题解。

……

原来只要稍加分析就能得出可从长度的唯一性来判断。自己还是思考太少,动手太匆忙……

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=5e4+15,fx=5,mod=998244353;
int n,m,a[N],tp[N],f[N],len[N],cd[N],ans,cnt[N];
//int g[N],p[N],c[N],prn;
int rdn()
{
  int ret=0,fx=1; char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-') fx=-1; ch=getchar();}
  while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
  return ret*fx;
}
void upd(int &x){x-=(x>=mod?mod:0);}
void query(int x,int &l/*,int &k*/)
{
  for(;x>0+fx;x-=(x&-x))
    {
      if(f[x]>l)
    {
      l=f[x];
      // k=g[x];
    }
      //      else if(f[x]==l) k+=g[x],upd(k);
    }
}
void add(int x,int l/*,int k*/)
{
  for(;x<=m+fx;x+=(x&-x))
    {
      if(l>f[x])
    {
      f[x]=l;
      // g[x]=k;
    }
      //      else if(l==f[x]) g[x]+=k,upd(g[x]);
    }
}
int main()
{
  n=rdn();
  for(int i=1;i<=n;i++) a[i]=tp[i]=rdn();
  sort(tp+1,tp+n+1); m=unique(tp+1,tp+n+1)-tp-1;
  for(int i=1;i<=n;i++) a[i]=lower_bound(tp+1,tp+n+1,a[i])-tp;
  for(int i=n;i;i--)
    {
      query(m-a[i]+fx,len[i]/*,p[i]*/);//not +1 for yan ge
      //      if(!len[i]) p[i]=1; 
      len[i]++;
      add(m-a[i]+1+fx,len[i]/*,p[i]*/);
      if(len[i]>ans){ans=len[i];/*prn=p[i];*/}
      //      else if(len[i]==ans) prn+=p[i],upd(prn);
    }
  memset(f,0,sizeof f);
  //  memset(g,0,sizeof g);
  for(int i=1;i<=n;i++)
    {
      query(a[i]-1+fx,cd[i]/*,c[i]*/);//a[i]-1!!!
      //      if(!cd[i]) c[i]=1;
      cd[i]++;
      if(len[i]+cd[i]==ans+1)cnt[cd[i]]++;
      add(a[i]+fx,cd[i]/*,c[i]*/);
    }
  printf("A:");
  for(int i=1;i<=n;i++)
    if(len[i]+cd[i]==ans+1&&cnt[cd[i]]>1/*&&(ll)p[i]*c[i]%mod<prn*/)
      printf("%d ",i);
  printf("\n");
  printf("B:");
  for(int i=1;i<=n;i++)
    if(len[i]+cd[i]==ans+1&&cnt[cd[i]]==1/*&&(ll)p[i]*c[i]%mod==prn*/)
      printf("%d ",i);
  printf("\n");
  return 0;
}

 

转载于:https://www.cnblogs.com/Narh/p/9642994.html

### 关于51Nod 3100 上台阶问题的C++解法 #### 题目解析 该题目通常涉及斐波那契数列的应用。假设每次可以走一步或者两步,那么到达第 \( n \) 层台阶的方法总数等于到达第 \( n-1 \) 层和第 \( n-2 \) 层方法数之和。 此逻辑可以通过动态规划来解决,并且为了防止数值过大,需要对结果取模操作(如 \( \% 100003 \)[^1])。以下是基于上述思路的一个高效实现: ```cpp #include <iostream> using namespace std; const int MOD = 100003; long long f[100010]; int main() { int n; cin >> n; // 初始化前两项 f[0] = 1; // 到达第0层有1种方式(不移动) f[1] = 1; // 到达第1层只有1种方式 // 动态规划计算f[i] for (int i = 2; i <= n; ++i) { f[i] = (f[i - 1] + f[i - 2]) % MOD; } cout << f[n] << endl; return 0; } ``` 以上代码通过数组 `f` 存储每层台阶的结果,利用循环逐步填充至目标层数 \( n \),并最终输出结果。 --- #### 时间复杂度分析 由于仅需一次线性遍历即可完成所有状态转移,时间复杂度为 \( O(n) \)。空间复杂度同样为 \( O(n) \),但如果优化存储,则可进一步降低到 \( O(1) \): ```cpp #include <iostream> using namespace std; const int MOD = 100003; int main() { int n; cin >> n; long long prev2 = 1, prev1 = 1, current; if (n == 0 || n == 1) { cout << 1 << endl; return 0; } for (int i = 2; i <= n; ++i) { current = (prev1 + prev2) % MOD; prev2 = prev1; prev1 = current; } cout << prev1 << endl; return 0; } ``` 在此版本中,只保留最近两个状态变量 (`prev1`, `prev2`) 来更新当前值,从而节省内存开销。 --- #### 输入输出说明 输入部分接受单个整数 \( n \),表示台阶数量;程序会返回从地面走到第 \( n \) 层的不同路径数目,结果经过指定模运算处理以适应大范围数据需求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值