• LOJ 6433 「PKUSC2018」最大前缀和——状压DP


    题目:https://loj.ac/problem/6433

    想到一个方案中没有被选的后缀满足 “该后缀的任一前缀和 <=0 ”。

    于是令 dp[ S ] 表示选了点集 S ,满足任一前缀和 <=0 的方案。很好转移。

    令 f[ S ] 表示选了点集 S ,且 S 整体就是最大前缀和的方案。

    只会 3n 做出 f[ ] ,就是考虑容斥, ( f[s]=|s|! - sum f[d]*dp[s^d] (sm[d]>=sm[s]) ) ,其中 sm[ s ] 表示点集 s 的权值和。

    然后看了题解。发现 “ S 整体是最大前缀和 ” <==> “ S 的任一后缀和 >0 ” ,所以像做 dp[ ] 一样,从后往前放数字就能转移了!!!

    还要训练思维。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int Mx(int a,int b){return a>b?a:b;}
    const int N=25,M=(1<<20)+5,mod=998244353;
    int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;}
    
    int n,a[N],dp[M],f[M]; ll sm[M];
    int ct[M],bin[N],lg[M],jc[N];
    namespace S1{
      void solve(int cnt)
      {
        int ans=0;for(int i=1;i<=n;i++)ans=upt(ans+a[i]);
        for(int i=1;i<=n;i++)ans=(ll)ans*i%mod;
        if(!cnt||n==1){printf("%d
    ",ans); return;}//n==1!!
        int rt,U=bin[n]-1;
        for(int i=1;i<=n;i++)if(a[i]<0){rt=i-1;break;}//i-1 for bin[]
        for(int s=0;s<U;s++)//s<U
          if((s&bin[rt])&&sm[s]<0)
        ans=upt(ans-(ll)jc[ct[U^s]]*jc[ct[s]-1]%mod*sm[s]%mod);
        printf("%d
    ",ans);
      }
    }
    namespace S2{
      int dp[M][85],mn[M],mx[M],fx=40;
      void init()
      {
        for(int s=1;s<bin[n];s++)
          for(int i=1;i<=n;i++)
        if(s&bin[i-1])
          {if(a[i]>0)mx[s]+=a[i];else mn[s]+=a[i];}
      }
      void solve()
      {
        init(); dp[0][0]=1;int U=bin[n]-1;
        mn[0]=mx[0]=-40;///
        //0 not 0+fx for 'none' can't be cal
        for(int s=0;s<U;s++)
          {
        int tp=U^s,d;
        while(tp)
          {
            d=tp&-tp; tp^=d; d|=s;
            for(int i=mn[s];i<=mx[s];i++)
              if(dp[s][i+fx])
            {
              int j=Mx(i,sm[d])+fx;
              dp[d][j]=upt(dp[d][j]+dp[s][i+fx]);
            }
          }
          }
        int ans=0;
        for(int i=mn[U];i<=mx[U];i++)
          if(dp[U][i+fx])
        ans=(ans+(ll)dp[U][i+fx]*i)%mod;
        printf("%d
    ",upt(ans));//upt
      }
    }
    void init()
    {
      bin[0]=1; lg[1]=0;
      for(int i=1;i<=n;i++)
        bin[i]=bin[i-1]<<1,lg[bin[i]]=i;
      jc[0]=1;for(int i=1;i<=n;i++)jc[i]=(ll)jc[i-1]*i%mod;
      for(int s=1;s<bin[n];s++)
        {
          sm[s]=sm[s^(s&-s)]+a[lg[s&-s]+1];
          ct[s]=ct[s^(s&-s)]+1;
        }
    }
    int main()
    {
      scanf("%d",&n); bool fg=0;int cnt=0;
      for(int i=1;i<=n;i++)
        {
          scanf("%d",&a[i]);
          if(a[i]<-2||a[i]>2)fg=1;
          if(a[i]<0)cnt++;
        }
      init();
      if(cnt<=1){S1::solve(cnt);return 0;}
      if(!fg){S2::solve();return 0;}
      dp[0]=1;
      for(int s=1;s<bin[n];s++)
        {
          if(sm[s]>0)continue;
          for(int i=1;i<=n;i++)
        if(s&bin[i-1])
          {
            int d=s^bin[i-1];
            if(dp[d])dp[s]=upt(dp[s]+dp[d]);
          }
        }
      f[0]=1;
      for(int s=1;s<bin[n];s++)
        for(int i=1;i<=n;i++)
          if(s&bin[i-1])
        {
          int d=s^bin[i-1];
          if(sm[d]>0||!d)f[s]=upt(f[s]+f[d]);//!d
        }
      int ans=0;
      for(int s=1,U=bin[n]-1;s<bin[n];s++)
        if(f[s]&&dp[U^s])
          ans=(ans+sm[s]%mod*f[s]%mod*dp[U^s])%mod;
      printf("%d
    ",upt(ans));
      return 0;
    }
  • 相关阅读:
    CSP-S2019 括号树
    [CQOI2007]余数求和
    CF1000E We Need More Bosses
    [HAOI2009]毛毛虫
    ls命令
    HTML的标签 属性 等等
    虚拟机安装Tools
    1.1 什么是安全渗透
    004-Kali Linux安装-熟悉环境
    003-Kali Linux 安装-持久加密USB安装、熟悉环境、熟悉BASH命令
  • 原文地址:https://www.cnblogs.com/Narh/p/10903808.html
Copyright © 2020-2023  润新知