zoukankan      html  css  js  c++  java
  • DP总结(优化等)

    留两个坑:1、测试51or(Get) 2、杂题3最大前缀和 (3、测试48group)4、相逢是问候(这个非DP),分手是祝愿。测试53,55。

    树DP感觉不像传统DP,所以分着写了。详见树总结。

    分着写太麻烦,还是放在最后了。

    欢迎大家在评论区提供新的方法!

    FIR:优化

    一决策点信息维护

      1:单调性:单调队列、栈。

            常见斜率,凸壳。单调性可以二分。

         T:1、测试50施工。单调栈。

       

    #include<bits/stdc++.h>
    #define F(i,a,b) for(rg int i=a;i<=b;++i)
    #define rg register
    #define LL long long
    #define il inline
    #define pf(a) printf("%d ",a)
    #define PF(a) printf("%lld ",a)
    #define phn puts("")
    using namespace std;
    #define int LL 
    int read();
    /*
    之前是(double)(b/(a*2))+0.5;
    改成(double)b/(a*2)+0.5
    或者1.0*b/(a*2)就A了。
    */
    #define N 1000010
    int n;LL c;
    int h[N];
    LL f[N];
    const LL inf=1e15;
    LL s1[N],s2[N];
    int sta[N],top;
    il LL cal(int i,int j,int mh){
        LL a=(i-j-1),b=2ll*(s1[i-1]-s1[j]),u=s2[i-1]-s2[j];
        if(j!=0){
            b+=c;u+=c*h[j];
        }
        if(i!=n+1){
            b+=c;u+=c*h[i];
        }
        LL t=1.0*b/(a*2)+0.5;
        t=max(1ll*mh,t);
        t=min(t,1ll*min(h[i],h[j]));
        return f[j]+t*t*a-t*b+u;
    }
    signed main(){
        n=read();c=read();
        F(i,1,n)h[i]=read();
        h[0]=h[n+1]=2e6;
        F(i,1,n){
            s1[i]=s1[i-1]+h[i];
            s2[i]=s2[i-1]+1ll*h[i]*h[i];
        }
        sta[top=1]=0;
        f[1]=0;sta[++top]=1;
        LL w;
        //PF(cal(4,2,h[3]));phn;return 0;
        F(i,2,n+1){
            f[i]=i==n+1?f[i-1]:f[i-1]+c*abs(h[i]-h[i-1]);
            while(top>1&&h[sta[top]]<=h[i]){
                w=cal(i,sta[top-1],h[sta[top]]);
                f[i]=min(f[i],w);
                --top;
            }
            sta[++top]=i;
        }
    //    F(i,1,n+1)PF(f[i]);phn;
        printf("%lld
    ",f[n+1]);
    }
    il int read(){
        int s=0,f=0;char ch;
        while(ch=getchar(),ch=='-'?f=1:0,!isdigit(ch));
        for(;isdigit(ch);s=s*10+(ch^48),ch=getchar());
        return f?-s:s;
    }
    /*
    g++ 1.cpp -g
    ./a.out
    4 2
    1 3 2 4
    */
    View Code

          2、测试51function。离线,单调栈维护斜率下凸壳,二分。  

    #include<bits/stdc++.h>
    #define F(i,a,b) for(rg int i=a;i<=b;++i)
    #define rg register
    #define LL long long
    #define il inline
    #define pf(a) printf("%lld ",a)
    #define phn puts("")
    using namespace std;
    #define int LL 
    #define N 500010
    int read();
    int n,que;
    int s[N],a[N],c[N];
    int min(int x,int y){return x<y?x:y;}
    int max(int x,int y){return x>y?x:y;}
    struct Q{
        int x,y,id;
        friend bool operator <(const Q& a,const Q& b){return a.y<b.y||(a.y==b.y&&a.x<b.x);}
    }b[N];
    int sta[N],top,ans[N];
    double get(int i,int j){
        return 1.0*(c[j]-c[i])/(a[i]-a[j]);
    }
    void push(int x){
        while(top&&a[sta[top]]>=a[x])--top;
        while(top>1&&get(x,sta[top])>=get(sta[top],sta[top-1]))--top;
        /** 这里是凸包的维护。第二句:使交点在栈内单调递减。因为画图发现交点递减才成为凸包,否则上一条线可以去掉
        如果不去掉,决策会使用上一条线,导致挡住原本更优的决策
        所以下凸包的维护:1、斜率递增,pop掉斜率比他大的
                  2、交点递减,防止不优的线影响决策
        */
        sta[++top]=x;
    }
    /** 可以证明,超过边界的不合法的决策点不优*/
    int solve(int x,int y){
        int l=1,r=top,mid;
        while(l<r){
            mid=l+r>>1;
        //    if(y-sta[mid]+1>x){l=mid+1;continue;}
            if(get(sta[mid],sta[mid+1])>x-y)l=mid+1;
            else r=mid;
        }    
        l=sta[l];
    //    pf(l);pf(x);pf(y);phn;
        /*if(x==100&&y==7){
            F(i,1,top)pf(a[sta[i]]);phn;while(1);
        }*/
        return s[y]-s[l]+(x-y+l)*a[l];
    }
    signed main(){
    //    freopen("function2.in","r",stdin);    freopen("1.out","w",stdout);
        n=read(); F(i,1,n)s[i]=s[i-1]+(a[i]=read()),c[i]=i*a[i]-s[i];
        que=read(); F(i,1,que)b[i]=(Q){read(),read(),i};
        sort(b+1,b+que+1);
        int p=1;
        while(b[p].y==1){
            ans[b[p].id]=a[1]*b[p].x;++p;
        }    
        sta[top=1]=1;
        /**     s[y]0-s[i]+(x-y+i)*a[i] 
            先判两个端点。
        */
        int i,x,y;
        F(k,2,n){    
            push(k);
            if(top==1){
                i=sta[1];
                while(b[p].y==k){
                    ans[b[p].id]=a[k]*b[p].x;
                    ++p;
                }
                continue;
            }
            while(b[p].y==k){
                ans[b[p].id]=solve(b[p].x,k);
                ++p;
            }
        //    push(k);
        }    
        F(i,1,que)printf("%lld
    ",ans[i]);
    }
    int read(){
        int s=0,f=0;char ch;
        while(ch=getchar(),ch=='-'?f=1:0,!isdigit(ch));
        for(;isdigit(ch);s=s*10+(ch^48),ch=getchar());
        return f?-s:s;
    }
    /*
    g++ 1.cpp -g
    ./a.out
    g++ dp.cpp -g
    ./a.out
    10
    10 9 1 7 4 6 8 5 2 3
    10
    4 2
    100 2
    6 3
    1 4
    3 5
    1 7
    100 7 
    5 8
    2 9
    100 10
    */
    View Code

          3、测试88T1维护身高分成k段。斜率优化式子。 

    `  1.5:关于一类四边形不等式:

         定义比较复杂,但只需记住比较重要一点的是:

         二维DP中

         定义S(i,j)为F(i,j)最优决策点。

         当S(i,j-1)<=S(i,j)<=S(i+1,j)时,可以记录决策点,在S(i,j-1)到S(i+1,j)之间找S(i,j).

         把F(i,1,n)F(j,i,n)F(k,i,j)的n^3优化为n^2.

         T:1、石子合并 2、测试93二叉搜索树。

         同样适用于一维DP。一维则要维护单调队列,记录三元组(j,l,r)表示决策点j的适用区间。

         弹队首,弹队尾,放队尾,更新队尾范围。在队尾区间二分当前点适用区间的l。

     2:数据结构。线段树。用于维护决策点/dp值。维护若干相同的操作。

            进阶,可以线段树合并维护DP。

       T:一类线段树优化DP:1、队长快跑。2、测试92数对:a,b两个数组,有aj<=bii限制。

                   讨论决策点j<=min(a,b),和a<j<=b;线段树实现区间查询最大,单点修改,区间加。

                  3、测试87 bird:vector差分线段。线段树动态维护决策点最优。

                  4、测试90 角度斜率离散话,线段树区间最值。注意两个0特判。

    一二进制,状压

      fr :测试109 T1:转化。压缩。预处理。递推。继承。

              K条路压缩为二进制数,枚举当前状态每一位,&1位直接异或。k^2->k

              2进制减lowbit递推每个状态(仅&1位作用)。k->1.

              详见B&哥做法

    一数学优化

      1、NTT

      2、矩阵,结合律,快速幂性质。

        T:测试52涂色游戏 组合+矩阵快速幂

      3、数论性质优化

      如:原根优化为循环矩阵

    一改变状态定义

      1、之前一道求数列乘积取模方案数:gcd(等等)相同函数值相同,减少状态数。

      2、测试33赤壁情:绝对位置变为相对位置

    #include<bits/stdc++.h>
    #define rg register
    #define F(i,a,b) for(rg int i=a;i<=b;++i)
    #define LL long long
    #define il inline
    #define pf(a) printf("%d ",a)
    #define pd(a) printf("%.3lf ",a)
    #define phn puts("")
    using namespace std;
    int read();
    /*
    Lxt
    */
    int n,m,bmw;
    il void out(double ans){
        int k=bmw;
        if(k==0)printf("%.0f
    ",ans);
        else if(k==1)printf("%.1lf
    ",ans);
        else if(k==2)printf("%.2lf
    ",ans);
        else if(k==3)printf("%.3lf
    ",ans);
        else if(k==4)printf("%.4lf
    ",ans);
        else if(k==5)printf("%.5lf
    ",ans);
        else if(k==6)printf("%.6lf
    ",ans);
        else if(k==7)printf("%.7lf
    ",ans);
        else if(k==8)printf("%.8lf
    ",ans);
    }
    double f[2][65][8005][4];
    #define dp f[p][j][k][l]
    il void work1(){
        f[n&1][0][0][0]=1;
        rg int p=0;
        //递推
        for(int i=n;i>=1;--i){
            p=i&1;
            F(j,0,52){
                F(k,0,7505){
                    F(l,0,2){
                        f[!p][j+1][k+i*2][l]+=dp*(j+1-l);
                        f[!p][j][k][l]+=dp*(j*2-l);
                        if(l<2){
                            f[!p][j+1][k+i][l+1]+=dp*(2-l);
                            if(k>=i)f[!p][j][k-i][l+1]+=dp*(2-l);
                        }
                        if(j>1&&k>=i*2)f[!p][j-1][k-i*2][l]+=dp*(j-1);
                        /*if(dp>1e-8){
                            if(j>1&&k>=i*2)printf("%d %d %d %d %d %.3f
    ",i,!p,j-1,k-i*2,l,f[!p][j-1][k-i*2][l]);
                        }*/
                        /*
                        与其他段交点:0:新段 1:延长 2:连接
                        0和1特殊讨论边界点
                        */
                        dp=0;
                    }
                }
            }
        }
        double ans=0;p=0;
        const int lxt=n*n/2;
        F(k,m,lxt){
            ans+=f[0][1][k][2];
        }
    //    F(k,2,3)pd(f[0][1][k][2]);
        F(i,2,n)ans/=i;
        out(ans);
    }
    int floor(__float128 x){
        for(int i=9;i>=0;--i)if(x>=i)return i;
    }
    void print__float128(__float128 x,int ws){
        int sta[55];sta[0]=0;
        for(int i=1;i<=ws;++i)x*=10,sta[i]=floor(x),x-=floor(x);
        x*=10;if(floor(x)>=5)sta[ws]++;
        for(int i=ws;i;--i)if(sta[i]==10)sta[i]=0,sta[i-1]++;
        printf("%d.",sta[0]);
        for(int i=1;i<=ws;++i)putchar(sta[i]+48);
        puts("");
    }
    __float128 g[2][28][2502][4];
    #define hp g[p][j][k][l]
    il void work_30(){
        g[n&1][0][0][0]=1;
        rg int p=0;
        //递推
        for(int i=n;i>=1;--i){
            p=i&1;
            F(j,0,26){
                F(k,0,2002){
                    F(l,0,2){
                        g[!p][j+1][k+i*2][l]+=hp*(j+1-l);
                        g[!p][j][k][l]+=hp*(j*2-l);
                        if(l<2){
                            g[!p][j+1][k+i][l+1]+=hp*(2-l);
                            if(k>=i)g[!p][j][k-i][l+1]+=hp*(2-l);
                        }
                        if(j>1&&k>=i*2)g[!p][j-1][k-i*2][l]+=hp*(j-1);
                        hp=0;
                    }
                }
            }
        }
        __float128 ans=0;p=0;
        const int lxt=n*n/2;
        F(k,m,lxt){
            ans+=g[0][1][k][2];
        }
    //    F(k,2,3)pd(g[0][1][k][2]);
        F(i,2,n)ans/=i;
        print__float128(ans,bmw);
    }
    int main(){
        n=read();m=read();bmw=read();
        if(bmw<=8)work1();
        else work_30();
    }
    il int read(){
        int s=0,f=0;rg char ch;
        while(ch=getchar(),ch=='-'?f=1:0,!isdigit(ch));
        for(;isdigit(ch);s=s*10+(ch^48),ch=getchar());
        return f?-s:s;
    }
    /*
    g++ 1.cpp -g
    time ./a.out
    50 200 30
    */
    /*
    
    */
    View Code

      3、测试49折射:按照另一种顺序,时间复杂转移简单变为时间简单转移复杂

    #include<bits/stdc++.h>
    #define F(i,a,b) for(rg int i=a;i<=b;++i)
    #define rg register
    #define LL long lnog
    #define il inline
    #define pf(a) printf("%d ",a)
    #define phn puts("")
    using namespace std;
    int read();
    /*
    数组尽量开大
    5000~5400有3个点
    */
    int n;
    const int mod=1e9+7;
    #define N 6001
    struct node{
        int x,y;
        friend bool operator<(const node &a,const node &b){
            return a.x<b.x;
        }
    }s[N];
    int b[N];
    il int MO(const int x){return x<mod?x:x-mod;}
    int f[6002][2];
    int main(){
        n=read();
        F(i,1,n){
            s[i].x=read();
            s[i].y=read();
        }
        sort(s+1,s+n+1);
        int ans=0;
        rg int y,w;    
        F(i,1,n){
            f[i][0]=f[i][1]=1;
            for(int j=i-1;j>=1;--j){
                if(s[j].y<s[i].y){
                    f[i][0]=MO(f[i][0]+f[j][1]);
                }
                else{
                    f[j][1]=MO(f[j][1]+f[i][0]);
                }
            }
        }
        F(i,1,n){
            ans=MO(ans+f[i][0]);ans=MO(ans+f[i][1]);
        //    printf("%d %d
    ",f[i][0],f[i][1]);
        }
        ans=(ans-n+mod)%mod;
        printf("%d
    ",ans);
    }
    il int read(){
        int s=0,f=0;char ch;
        while(ch=getchar(),ch=='-'?f=1:0,!isdigit(ch));
        for(;isdigit(ch);s=s*10+(ch^48),ch=getchar());
        return f?-s:s;
    }
    /*
    g++ 2.cpp -g
    ./a.out
    4
    2 2
    3 1
    1 4
    4 3
    */
    View Code

    一枚举方式。

      背包类一维范围小,一维范围极大,f数值小时,调换大范围一维和小范围结果。

      T:测试92口胡题T3。(折半思想。这题折半可以10+20,而非15+15)

    一网格/二维

      1、网格DP

        插头DP是一类网格DP。

        网格DP的一般做法是枚举n*m,扫一行,继承上一行信息。

        复杂度n*m*状态数。

        一般搭配状压使用。

        T:测试59的部分分(全场两人拿到这个子任务,另外一个是正解)

    #include<bits/stdc++.h>
    #define F(i,a,b) for(int i=a;i<=b;++i)
    #define LL long long
    #define pf(a) printf("%d ",a)
    #define phn puts("")
    using namespace std;
    int n,all;
    #define N 100010
    int a[N],b[N];
    LL f[2][2][1000010];
    const int mod=1e9+7;
    void cal(int x,int y){
        int p=((x-1)*n+y)&1,lm=min(a[x],b[y]);
        int u,v;
        for(int i=all;i>=0;--i){
            (f[p][0][i]+=f[!p][0][i]*lm)%=mod;
            (f[p][1][i]+=f[!p][1][i]*lm)%=mod;
            if(a[x]==b[y]){
                (f[p][1][i|(1<<(y-1))]+=f[!p][0][i]+f[!p][1][i])%=mod;
            }
            else if(a[x]<b[y]){
                (f[p][1][i]+=f[!p][0][i]+f[!p][1][i])%=mod;
            }
            else if(a[x]>b[y]){
                (f[p][0][i|(1<<(y-1))]+=f[!p][0][i])%=mod;
                (f[p][1][i|(1<<(y-1))]+=f[!p][1][i])%=mod;
            }
            f[!p][0][i]=f[!p][1][i]=0;
        }
    }
    void work1(){
        f[0][1][0]=1;all=(1<<n)-1;
        int p;
        F(i,1,n){
            p=((i-1)*n)&1;
            F(j,0,all){
                f[p][0][j]=f[p][1][j];f[p][1][j]=0;
            }
            F(j,1,n){
                cal(i,j);
            }
        }
        p=(n*n)&1;
        printf("%lld
    ",f[p][1][all]);
    }
    int qpow(LL x,int k){LL s=1;for(;k;x=x*x%mod,k>>=1)if(k&1)s=s*x%mod;return s;}
    int main(){
        scanf("%d",&n);F(i,1,n)scanf("%d",&a[i]);F(i,1,n)scanf("%d",&b[i]);
        if(n<=16)work1();
        else{
            LL ans=1;
            F(i,1,n){
                LL w=(qpow(i+1,n-i)-qpow(i,n-i)+mod)%mod;
                w=w*w%mod;
                ans=(w*i+qpow(i+1,2*(n-i)))%mod*ans%mod;
            }
            printf("%lld
    ",ans);
        }
    }
    /*
    g++ 2.cpp -g
    ./a.out
    3
    1 2 3
    1 2 3
    */
    View Code

      2、插头,轮廓线

       插头DP是网格+轮廓线。

       而轮廓线本身是一个独立的技巧。

       轮廓线可用Hash表优化状态数。

       来自学长的两道插头DP题的模板:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define F(i,a,b) for(int i=a;i<=b;++i)
    #define LL long long
    #define rg register
    #define phn printf("
    ")
    #define pf(a) printf("%d ",a)
    #define PF(a) printf("%lld ",a)
    #define il inline
    using namespace std;
    int read();
    int n,m;
    const int cube=(int)1e9,mod=10007;
    #define me(a) memset(a,0,sizeof(a))
    struct data{
        int bit[6];
        il void Clear(){bit[0]=bit[1]=bit[2]=bit[3]=bit[4]=bit[5]=0;}
        data(){Clear();}
        il void Set(int x){Clear();while(x){bit[++bit[0]]=x%cube;x/=cube;}}
        il int &operator [](int x){return bit[x];}
        il void print(){
            printf("%d",bit[bit[0]]);
            for(int i=bit[0]-1;i>=1;--i)printf("%09d",bit[i]);phn;
        }
        il data operator + (data b){
            data c;c.Clear();
            c[0]=max(bit[0],b[0])+1;
            F(i,1,c[0]){
                c[i]+=bit[i]+b[i];c[i+1]=c[i]/cube;c[i]%=cube;
            }
            while(!c[c[0]])--c[0];
            return c;
        }
        il void operator +=(data b){*this=*this+b;}
        il void operator = (int x){Set(x);} 
    }ANS;
    struct hash{
        data val[mod];
        int head[mod],size,sta[mod],fir[mod];
        il void init(){
            me(val);me(sta);me(fir);me(head);size=0;
        }
        il data &operator [] (const int &state){
            int pos=state%mod,i;
            for(i=head[pos];i&&sta[i]!=state;i=fir[i]);
            if(!i){sta[++size]=state;fir[size]=head[pos];head[pos]=size;i=size;}
            return val[i];
        }    
    }f[2];
    il int find(int sta,int id){return (sta>>((id-1)<<1))&3;}
    il void set(int &sta,int id,int val){
        id=(id-1)<<1;sta|=3<<id;sta^=3<<id;sta|=val<<id;
    }    
    il int link(int sta,int pos){
        int cnt=0,g=(find(sta,pos)==1)?1:-1;
        for(int i=pos;i&&i<=m+1;i+=g){   //??m+1
            int plug=find(sta,i);
            if(plug==1)++cnt;
            else if(plug==2)--cnt;
            if(cnt==0)return i;
        }
        return -1;
    }
    il void cal(int x,int y){
        int now=((x-1)*m+y)&1,last=now^1,tot=f[last].size;
        f[now].init();
        F(i,1,tot){
            int sta=f[last].sta[i];data val=f[last].val[i];
            int p1=find(sta,y),p2=find(sta,y+1);
            if(link(sta,y)==-1||link(sta,y+1)==-1)continue;
        //    pf(x);pf(y);phn;
            if(!p1&&!p2){
                if(x!=n&&y!=m){
                    set(sta,y,1);set(sta,y+1,2);f[now][sta]+=val;
                }        
            }
            else if(p1&&!p2){
                if(x!=n)f[now][sta]+=val;
                if(y!=m)set(sta,y,0),set(sta,y+1,p1),f[now][sta]+=val;
            }
            else if(p2&&!p1){
                if(y!=m)f[now][sta]+=val;
                if(x!=n)set(sta,y+1,0),set(sta,y,p2),f[now][sta]+=val;
            }
            else {
                if(p1==1&&p2==2){if(x==n&&y==m){ANS+=val;}}
                else if(p1==2&&p2==1){
                    set(sta,y,0);set(sta,y+1,0);f[now][sta]+=val;
                }
                else if(p1==1&&p2==1){
                    int pos=link(sta,y+1);
                    set(sta,y,0);set(sta,y+1,0);set(sta,pos,1);
                    f[now][sta]+=val;
                }
                else if(p1==2&&p2==2){
                    int pos=link(sta,y);
                    set(sta,y,0);set(sta,y+1,0);set(sta,pos,2);
                    f[now][sta]+=val;
                }
            }
        }
    }
    int main(){
        n=read();m=read();
        if(n==1||m==1){puts("1");return 0;}
        if(m>n){m^=n^=m^=n;}//
        f[0].init();f[0][0]=1;
        F(i,1,n){
            F(j,1,m)cal(i,j);
            if(i!=n){
                int now=(i*m)&1,tot=f[now].size;
                F(j,1,tot)f[now].sta[j]<<=2;
            }
        }
        ANS+=ANS;ANS.print();
    }
    il int read(){
        int s=0,f=0;char ch;
        while(ch=getchar(),ch=='-'?f=1:0,ch<'0'||ch>'9');
        while(ch>='0'&&ch<='9'){s=s*10+(ch^48);ch=getchar();}
        return s;
    }
    /*
    g++ 1.cpp -g
    ./a.out
    2 2
    */
    邮递员,(手写高精,hash表)
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define F(i,a,b) for(int i=a;i<=b;++i)
    #define LL long long
    #define rg register
    #define cri const rg int
    #define phn printf("
    ")
    #define pf(a) printf("%d ",a)
    #define PF(a) printf("%lld ",a)
    #define il inline
    using namespace std;
    int read();
    int n,m;
    //易错索引:h58,i<=m+1(轮廓线是m+1)
    const int cube=(int)1e9,mod=30007,NX=120007;
    #define ME(a) memset(a,0,sizeof(a))
    LL ans;
    LL valu[2][NX];
    int head[2][mod],size[2],state[2][NX],fir[2][NX];
    il void init(int g){
        memset(head[g],0,sizeof(head[g]));size[g]=0;//me(sta);me(fir);
    }
    il void add (const int &sta,const LL w,const int g){
        int pos=sta%mod,i;
        for(i=head[g][pos];i;i=fir[g][i]){
            if(state[g][i]==sta){valu[g][i]+=w;return;}
        }
        state[g][++size[g]]=sta;fir[g][size[g]]=head[g][pos];head[g][pos]=i=size[g];valu[g][i]=w;
    }    
    il int find(rg int sta,cri id){return (sta>>((id-1)<<1))&3;}
    il void set(rg int &sta,rg int id,int val){
        id=(id-1)<<1;sta|=3<<id;sta^=3<<id;sta|=val<<id;
    }    
    il int link(cri sta,cri pos){
        int cnt=0,g=(find(sta,pos)==1)?1:-1;
        for(int i=pos;i&&i<=m+1;i+=g){   //m+1:
            int plug=find(sta,i);
            if(plug==1)++cnt;
            else if(plug==2)--cnt;
            if(cnt==0)return i;
        }
        return -1;
    }
    int a[42][42],endx,endy;
    bool comp;
    il void cal(const int x,const int y){
        int now=((x-1)*m+y)&1,last=now^1,tot=size[last];
        init(now);
        F(i,1,tot){
            rg int sta=state[last][i];LL val=valu[last][i];
            cri p1=find(sta,y),p2=find(sta,y+1);
        //    if(link(sta,y)==-1||link(sta,y+1)==-1)continue;
        //    pf(x);pf(y);phn;
            if(a[x][y]){
                if(!p1&&!p2)add(sta,val,now);
                continue;
            }
            if(!p1&&!p2){
                if(x!=n&&y!=m&&!a[x+1][y]&&!a[x][y+1]){
                    set(sta,y,1);set(sta,y+1,2);add(sta,val,now);
                }        
            }
            else if(p1&&!p2){
                if(x!=n&&!a[x+1][y])add(sta,val,now);
                if(y!=m&&!a[x][y+1])set(sta,y,0),set(sta,y+1,p1),add(sta,val,now);
            }
            else if(p2&&!p1){
                if(y!=m&&!a[x][y+1])add(sta,val,now);
                if(x!=n&&!a[x+1][y])set(sta,y+1,0),set(sta,y,p2),add(sta,val,now);
            }
            else {
                if(p1==1&&p2==2){if(x==endx&&y==endy){ans+=val;comp=1;}}
                else if(p1==2&&p2==1){
                    set(sta,y,0);set(sta,y+1,0);add(sta,val,now);
                }
                else if(p1==1&&p2==1){
                    set(sta,link(sta,y+1),1);set(sta,y,0);set(sta,y+1,0);
                    add(sta,val,now);
                }
                else if(p1==2&&p2==2){
                    set(sta,link(sta,y),2);set(sta,y,0);set(sta,y+1,0);
                    add(sta,val,now);
                }
            }
        }
    }
    int main(){
        n=read();m=read();
    //    if(n==1||m==1){puts("1");return 0;}
    //    if(m>n){m^=n^=m^=n;}//
        char ch[20];
        F(i,1,n){scanf("%s",ch+1);F(j,1,m){if(ch[j]=='*')a[i][j]=1;else {endx=i;endy=j;}}}
        init(0);add(0,1,0);
        F(i,1,n){
            F(j,1,m){
                cal(i,j);
                if(comp==1){
                    printf("%lld",ans);return 0;
                }
            }
            if(i!=n){
                int now=(i*m)&1,tot=size[now];
                F(j,1,tot)state[now][j]<<=2;
            }
        }
        printf("%lld",ans);
    }
    il int read(){
        int s=0,f=0;char ch;
        while(ch=getchar(),ch=='-'?f=1:0,ch<'0'||ch>'9');
        while(ch>='0'&&ch<='9'){s=s*10+(ch^48);ch=getchar();}
        return s;
    }
    /*
    g++ 1.cpp -g
    ./a.out
    4 4
    **..
    ....
    ....
    ..**
    */
    Ural 1519 Formula 1,很多分类讨论

        考场想出的8进制压位记录各位置所属集合:

    #include<bits/stdc++.h>
    #define rg register 
    #define F(i,a,b) for(rg int i=a;i<=b;++i)
    #define LL long long
    #define pf(a) printf("%lld ",a)
    #define phn puts("")
    using namespace std;
    #define int LL
    #define N 600010
    LL n;
    int prm[8],cnt,a[8];
    int c[N],bin[N],bl[N];
    const int mod=1e9+7;
    int ask(int s,int pos){
        pos=(pos-1)*3;
        return (s>>pos)&7;
    }
    void sit(int &x,int pos,int w){
        pos=(pos-1)*3;
        x=((x|(7<<pos))^(7<<pos))|(w<<pos);
    }
    void MO(int &x){x<mod?x:x-=mod;}
    const int HP=233713;
    struct nd{
        LL val[N];
        int head[HP],to[N],fir[N],cnt;
        void clear(){
            cnt=0;memset(head,0,sizeof(head));
        }
        inline LL &operator[](const int s){
            int t=s%HP;
            for(int i=head[t];i;i=fir[i])if(to[i]==s)return val[i];
            to[++cnt]=s;fir[cnt]=head[t];
            return val[head[t]=cnt]=0;
        }
    }f[2];
    signed main(){
        scanf("%lld",&n);
        int x=n;
        for(int i=2,ed=sqrt(n);i<=ed;++i){
            if(x%i==0){
                ++cnt;
                while(x%i==0){    
                    x/=i,++a[cnt];
                }
            }
        }
        if(x>1)a[++cnt]=1;
        const int mx=(1<<(cnt*3))-1,ed=(1<<cnt)-1;
        c[0]=1;
        for(int i=1,j=1;j<=cnt;++j,i<<=1)bin[i]=j;
        F(i,1,ed){
            c[i]=c[i^(i&-i)]*a[bin[i&-i]]%mod;
    //        bl[i]=bin[i&-i];
        }
        f[0].clear();
        f[0][0]=1;rg int p=0;/** */
        LL ans=0;
        F(i,1,cnt*2){
            p=!p;
            f[p].clear();
            for(int u=1,id,ok,t,fir,s;u<=f[!p].cnt;++u){
                s=f[!p].to[u];
                MO(ans+=f[!p].val[u]);
                F(j,1,ed){
                    id=0,ok=1;t=s;fir=0;
                    F(k,1,cnt){
                        if((j>>(k-1))&1){
                            if(ask(s,k)==7){ok=0;break;}
                            if(ask(s,k)==0&&!fir)fir=k;
                            if(id==0){
                                id=ask(s,k);
                                if(id)sit(t,k,7);
                            }
                            else{
                                 if(ask(s,k)>0&&id!=ask(s,k)){ok=0;break;}
                                 else{
                                     if(ask(s,k)>0)sit(t,k,7);
                                 }
                            }
                        }
                    }
                    if(ok){
                        if(fir)F(k,1,cnt){
                            if((j>>(k-1))&1){
                                if(!ask(t,k))sit(t,k,fir);
                            }
                        }
                        f[p][t]=(f[p][t]+f[!p].val[u]*c[j])%mod;
            //            if(f[!p][s]){
                //            pf(i);pf(s);pf(j);pf(t);pf(f[p][t]);phn;    }
                    }    
                }
            }
        }
        for(int i=1;i<=f[p].cnt;++i)MO(ans+=f[p].val[i]);
        MO(ans=ans-1+mod);
        printf("%lld
    ",ans);
    }
    /*
    g++ 2.cpp -g -O2
    time ./a.out
    12156144
    
    */
    测试84 six

     3、差分思想。维护线段(一行信息)时只记录左端点。转移到下一行时看两行对应段划分方式,相同可以合并。

      

    #include<bits/stdc++.h>
    #define F(i,a,b) for(int i=a;i<=b;++i)
    #define pf(a) printf("%d ",a)
    #define phn puts("")
    using namespace std;
    int read();
    int n,m;
    #define N 105
    int a[105][105];
    int f[105][1<<8];
    const int inf=1e8;
    int main(){
        n=read();m=read();
        F(i,1,n)F(j,1,m)a[i][j]=read();
        int ed=(1<<m)-1;
        memset(f,0x3f,sizeof(f));
        int t,x;
        int c[10]={0};
        f[0][0]=0;
        F(i,0,n-1){
            for(int s=0,ok;s<=ed;++s){
                ok=1;
                F(j,1,m){
                    if(a[i][j]==0){
                        if((s>>(j-1))&1){
                            ok=0;break;
                        }    
                        c[j]=0;
                    }
                    else if(a[i][j]==1){
                        if((s>>(j-1))&1){
                            c[j]=j;
                        }
                        else if(c[j-1])c[j]=c[j-1];
                        else {ok=0;break;}
                    }
                }
                if(!ok)continue;
                for(int t=0,w,las;t<=ed;++t){
                    w=0;las=0;
                    F(j,1,m){
                        if(a[i+1][j]){//判结尾
                            if((t>>(j-1))&1){
                                ++w;
                                las=j;
                            }
                            else if(!las){
                                w=inf;break;
                            }
                            if(a[i+1][j+1]==0||((t>>(j+1-1))&1)){
                                if(las==c[j]&&c[j+1]!=c[j])--w;
                            }
                        }
                        else {
                            las=0;
                            if((t>>(j-1))&1){w=inf;break;}
                        }
                    }
                    f[i+1][t]=min(f[i+1][t],f[i][s]+w);
                }
            }
        }
        int ans=inf;
        F(s,0,ed){
            ans=min(ans,f[n][s]);
        }
        printf("%d
    ",ans);
    }
    int read(){
        int s=0,f=0;char ch=getchar();
        while(!isdigit(ch))f=ch=='-',ch=getchar();
        while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
        return f?-s:s;
    }
    /*
    g++ 2.cpp 
    ./a.out
    4 4
    1 1 1 0
    1 1 1 1
    0 0 1 1
    0 0 1 1
    */
    测试90邻面合并

    一小技巧:

      比较重要的:

            1、哈希表。对于可证明的状态数比较小,但是范围比较大的。

              DP可优化时间空间。记忆化dfs可优化空间。

              常用于状压、轮廓线。

              T:测试53 T2 状压期望

              (dfs写在hash里超帅的)

    #include<bits/stdc++.h>
    #define F(i,a,b) for(int i=a;i<=b;++i)
    #define LL long long
    #define PF(a) printf("%.6lf ",a)
    #define pf(a) printf("%d ",a)
    #define phn puts("")
    using namespace std;
    int read();
    int n,m;
    char c[50];
    const int mod=533317;
    inline double max(const double&a,const double&b){return a>b?a:b;}
    struct Hash{
        #define M 20000010
        double f[M];int vis[M];
        int head[32][mod],fir[M],to[M],cnt;
        inline int get(const int &x,const int &y){
            int t=y%mod;
            for(int i=head[x][t];i;i=fir[i])
                if(to[i]==y)return i;
            to[++cnt]=y;fir[cnt]=head[x][t];
            return head[x][t]=cnt;
        }
        int chg(int j,int pos){
            int z=j&((1<<pos)-1);
            return (j>>(pos+1)<<pos)|z;
        }
        double dfs(int i,int j){
            if(i==n-m)return 0;
            int t=get(i,j);
            if(vis[t])return f[t];
            vis[t]=1;
            F(k,0,i-1){
                f[t]+=max(dfs(i-1,chg(j,k))+((j>>k)&1),
                         dfs(i-1,chg(j,i-1-k))+((j>>(i-1-k))&1));
            }
            f[t]/=i;
            return f[t];
        }
    }q;
    int main(){
        //pf(chg((1<<5)-1-(1<<3),2));
        n=read();m=read();
        scanf("%s",c+1);
        int b=0;
        F(i,1,n)c[i]=='W'?(c[i]=1,b|=(1<<(i-1))):c[i]=0;
        double ans=q.dfs(n,b);
        printf("%.6lf
    ",ans);
    }
    int read(){
        int s=0,f=0;char ch;
        while(ch=getchar(),ch=='-'?f=1:0,!isdigit(ch));
        for(;isdigit(ch);s=s*10+(ch^48),ch=getchar());
        return f?-s:s;
    }
    /*
    g++ 2.cpp -g 
    ./a.out
    4 2 WBWB
    */
    View Code

            2、拆式子。如拆abs(讨论大小关系)、min

      不很重要的:1、前缀和

            2、倒着扫

    SEC:写法

    a:

    思路导引:

      猜测约束(条件)用途。根据约束发现性质,使状态可以被简化。(构造计数类)

      根据数据范围考虑状态框架。

    一、概率期望类

      一般倒着转移。因为一般初状态一定,末状态有很多

      1、最优性期望:

        T:测试53T2。最优策略,直接在dfs时取两个下一步状态的max

      2、期望性质:

        测试32 chemistry:  (osu树上高次版)

        E(x+y)=E(x)+E(y)

        E(x&&y)=E(x)*E(y)

        E((x+y)^j)=sigma C(j,k)*E(x^k)*E(y^(j-k))

        把多次方的期望拆分。考虑两两合并产生的贡献。

        详见之前写的题解。

    #include<bits/stdc++.h>
    #define F(i,a,b) for(rg int i=a;i<=b;++i)
    #define rg register
    #define pf(a) printf("%lld ",a)
    #define phn puts("")
    #define LL long long
    #define il inline
    using namespace std;
    #define int LL
    int read();
    #define N 200010
    int n,m,p,q;
    const int mod=1e9+7;
    int a[N],f[N][12],g[N][12],C[15][15];
    int to[N<<1],fir[N<<1],head[N<<1],cnt;
    il int qpow(int x,int k){int s=1;for(;k;x=x*x%mod,k>>=1)if(k&1)s=s*x%mod;return s;}
    il void add(int x,int y){
        to[++cnt]=y;fir[cnt]=head[x];head[x]=cnt;
    }
    void dfs(int x,int fa){
        g[x][0]=1;f[x][0]=1;
        g[x][1]=p*a[x]%mod;f[x][1]=p*a[x]%mod;
        F(i,2,m){
            g[x][i]=g[x][i-1]*a[x]%mod;
            f[x][i]=f[x][i-1]*a[x]%mod;
        }
        for(int i=head[x];i;i=fir[i]){
            int v=to[i];
            if(v==fa)continue;
            dfs(v,x);
            for(int j=m;j>=1;--j){
                (f[x][j]+=f[v][j])%=mod;
                F(k,1,j-1){
                    (f[x][j]+=C[j][k]*g[x][k]%mod*g[v][j-k]%mod)%=mod;
                }
                //f[x][j]%=mod;
            }
            for(int j=m;j>=1;--j){
                (g[x][j]+=p*g[v][j])%=mod;
                F(k,1,j-1){
                    (g[x][j]+=C[j][k]*g[x][k]%mod*g[v][j-k]%mod)%=mod;
                }
                //g[x][j]%=mod;
            }
        }
    }
    signed main(){
        C[0][0]=1;
        F(i,1,12){
            C[i][0]=1;
            F(j,1,i)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        }
        n=read();m=read();p=read();q=read();
        p=p*qpow(q,mod-2)%mod;
        int x,y;
        F(i,1,n)a[i]=read();
        F(i,2,n){
            x=read();y=read();add(x,y);add(y,x);
        }
        dfs(1,0);
        printf("%lld
    ",f[1][m]);
    }
    il int read(){
        int s=0;rg char ch;
        while(ch=getchar(),!isdigit(ch));
        for(;isdigit(ch);s=s*10+(ch^48),ch=getchar());
        return s;
    }
    /*
    g++ 1.cpp -g
    time ./a.out
    3 2 1 2
    1 2 4
    1 2
    1 3
    */
    
    复制代码
    View Code

       

    二、组合类

      T:测试52 涂色游戏 组合+矩阵

        这里主要想说其中一个系数。我用容斥做的,但是也可以递推求。两种写法。

      另附:对于一类容斥系数:

         要求恰好j种,容斥减去子集。

         系数:j:1  (C(j,j)) 

            j-1:-1*C(j,j-1)   

            j-2: 考虑上两个的贡献

              -1*C(j,j-2)+C(j,j-1)*C(j-1,j-2)

              化简后是 C(j,j-2)  ,含义没有弄清楚。感性上似乎可以理解。还是感性理解吧。

           j-k:经过归纳,发现是-1^(j-k)*C(j,j-k)

         (19,10,4)add:组合数部分可以看成不是容斥系数,而是表达式一部分。

               这样容斥系数就是+-1了。

    #include<bits/stdc++.h>
    #define F(i,a,b) for(int i=a;i<=b;++i)
    #define LL long long
    #define pf(a) printf("%lld ",a)
    #define PF(a) printf("%lld ",a)
    #define phn puts("")
    using namespace std;
    #define int LL
    int read();
    /** 为什么可以矩阵
    取模优化错了。
    有一个地方有减法,忘了+mod,出负数了。
    有减法的地方一定要+mod
    取模优化很快,几乎快了二倍,但是减法不加modu会出bug
    */
    int n,m,p,q;
    const int mod=998244353;
    int f[105],c[105][105],s[105][105],a[105];
    int max(int x,int y){return x>y?x:y;}
    int MO(const int x){return x<mod?x:x-mod;}
    int qpow(int x,int k){int s=1;for(;k;x=x*x%mod,k>>=1)if(k&1)s=s*x%mod;return s;}
    /* int D[105][105];
    int ans;
    int T[100];
    void dfs(int y,int x){
        if(y==m+1){++ans;return ;}
        int tx=x==n?1:x+1,ty=x==n?y+1:y;
        F(i,1,p){
            D[x][y]=i;
            if(x==n&y>1){
                int cnt=0;
                F(i,1,n)F(j,y-1,y)if(D[i][j]&&(!T[D[i][j]]))T[D[i][j]]=1,++cnt;
                F(i,1,n)F(j,y-1,y)T[D[i][j]]=0;
                if(cnt<q)continue;
            }    
            dfs(ty,tx);
        }
    }*/
    void jzchs(){
        const int ed=min(p,n);
        int g[105][105]={0};
        F(k,1,ed){
            F(i,1,ed){
                F(j,1,ed){
                    g[i][j]=MO(g[i][j]+s[i][k]*s[k][j]%mod);
                }
            }
        }
        F(i,1,ed)F(j,1,ed)s[i][j]=g[i][j];
    }
    void jzcc(){    
        const int ed=min(p,n);
        int g[105]={0};
        F(k,1,ed){
            F(j,1,ed){
                g[j]=MO(g[j]+f[k]*s[k][j]%mod);
            }
        }
        F(j,1,ed)f[j]=g[j];
    }
    signed main(){
        n=read();m=read();p=read();q=read();
    //    dfs(1,1);printf("%lld
    ",ans);
        c[0][0]=s[0][0]=1;
        F(i,1,100){
            c[i][0]=1;
            F(j,1,i)c[i][j]=MO(c[i-1][j-1]+c[i-1][j]);
        }
        F(j,0,100){
            F(k,1,j)a[j]=MO(a[j]+c[j][k]*qpow(k,n)*((j-k)&1?-1:1)%mod+mod);
        }
        const int ed=min(p,n);
        F(k,1,ed){
            F(j,max(1,q-k),ed){
                F(t,0,j+k-q)s[k][j]=MO(s[k][j]+c[p-k][j-t]*c[k][t]%mod);
                s[k][j]=s[k][j]*a[j]%mod; /** */
            }
        }    
        F(j,1,ed)f[j]=c[p][j]*a[j]%mod;
        for(int k=m-1;k;k>>=1,jzchs())if(k&1)jzcc();
        /** 这里是m-1.*/
    /*    F(i,2,m){
            F(j,1,ed){
                F(k,max(1,q-j),ed){
                    f[i][j]=MO(f[i][j]+f[i-1][k]*s[k][j]%mod);
                }
            //    f[i][j]=f[i][j]*a[j]%mod;
            }
        }
    //    F(i,1,m){    F(j,1,ed)pf(f[i][j]);phn; }/** */
        int ans=0;
        F(j,1,ed)ans=MO(ans+f[j]);
        ans=(ans+mod)%mod;
        printf("%lld
    ",ans);
    }
    int read(){
        int s=0,f=0;char ch;
        while(ch=getchar(),ch=='-'?f=1:0,!isdigit(ch));
        for(;isdigit(ch);s=s*10+(ch^48),ch=getchar());
        return f?-s:s;
    }    
    /*
    g++ 2.cpp -g
    ./a.out
    3 4 4 4
    */
    容斥
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define ll long long
    using namespace std;
    const int N=105;
    const int mod=998244353;
    int n,m,p,q;
    ll C[N][N],f[N][N],dp[N][N];//i种颜色 填入j个空位
    struct Mat{
        ll s[N][N];
        friend Mat operator *(const Mat &a,const Mat &b){
            Mat ans;
            for(int i=1;i<=p;++i)
                for(int j=1;j<=p;++j){
                    ans.s[i][j]=0;
                    for(int k=1;k<=p;++k) ans.s[i][j]=(ans.s[i][j]+a.s[i][k]*b.s[k][j])%mod;
                }
            return ans;
        }
        friend Mat operator ^(Mat base,int k){
            Mat ans;
            memset(ans.s,0,sizeof(ans.s));
            for(int i=1;i<=p;++i) ans.s[i][i]=1;
            while(k){
                if(k&1) ans=ans*base;
                base=base*base;
                k>>=1;
            }
            return ans;
        }
    }g,t;
    int main(){
        scanf("%d%d%d%d",&n,&m,&p,&q);
        for(int i=0;i<=p;++i){
            C[i][0]=1;
            for(int j=1;j<=i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        }
        f[0][0]=1;
        for(int i=1;i<=p;++i) for(int j=i;j<=n;++j) f[i][j]=(f[i][j-1]+f[i-1][j-1])*i%mod;
        for(int i=1;i<=p;++i) t.s[1][i]=C[p][i]*f[i][n]%mod;
        for(int j=1;j<=p;++j)
            for(int k=1;k<=p;++k){
                ll tot=0;
                for(int d=max(q-k,0);d<=j;++d) tot+=C[p-k][d]*C[k][j-d]%mod;
                tot%=mod;
                g.s[k][j]=tot*f[j][n]%mod;
            }
        t=t*(g^(m-1));
        ll ans=0;
        for(int i=1;i<=p;++i) ans+=t.s[1][i];
        printf("%lld
    ",ans%mod);
        return 0;
    }
    skyh的递推

    三、树相关DP

      1二叉树先序中序遍历性质。

        规定X序遍历序列,可知树性质。

        例:

      测试76C:计数

    先序,编号为 i的节点在二叉树的前序遍历中恰好是第 i个出现.

          则:1子树编号>u.

    2、若有左儿子,编号u+1.

    3、一棵子树中编号连续。

    4、左树编号<右树。

    本题另一个约束:m个中序遍历下u,v编号先后关系。(2

    统计合法带标号二叉树的个数。

    通过限制vu左右子树或不在同一子树可满足约束(2.

    归纳为v是否在u左子树。

    又,约束(1)提供子树编号连续,及子节点id较大性质。

    则可知u左子树sz范围。

    F[i][j],i为根子树,大小为j方案数。

    简单DP即可。

    N<=400,m<=1000,O(n^3

    回过来看m这个条件的用法。

    1、状态不能准确描述树的具体形态,

    2、约束2不能准确找到,且一个点所受约束个数不定,0~多个,无法一对一。则考虑约束2限定一些范围。而不是在转移时u->v一对一。

    (猜测用途)

    3N^3,考虑二维DP状态f[i][j].

    (根据数据范围考虑状态框架)

    4、最重要的是约束1,使id连续,且深度深id大。

    使可以根据左子树大小范围确定两点关系。

    (根据约束发现性质)

          Code:

    #include<bits/stdc++.h>
    #define F(i,a,b) for(int i=a;i<=b;++i)
    #define LL long long
    #define pf(a) printf("%lld ",a)
    #define phn puts("")
    using namespace std;
    #define int LL
    int read();
    #define N 505
    int n,m;
    int f[N][N];
    const int mod=1e9+7;
    int top[N],lea[N];//左子树
    void work(){
        n=read();m=read();
        int u,v;
        F(i,1,n){
            lea[i]=0;top[i]=n-i;
        }
        memset(f,0,sizeof(f));
        F(i,1,m){
            u=read();v=read();
            if(u<v){
                top[u]=min(top[u],v-1-u);
            }
            else{
                lea[v]=max(lea[v],u-v);
            }
        }
    //    F(i,1,n){        pf(lea[i]);pf(top[i]);phn;    }
        f[n][1]=1;f[n][0]=1;f[n+1][0]=1;
        for(int i=n-1,ed;i>0;--i){
            f[i][0]=1;
            if(lea[i]>top[i]){
                puts("0");return ;
            }
            F(j,lea[i]+1,n-i+1){
                ed=min(j-1,top[i]);
                F(k,lea[i],ed){
                    f[i][j]=(f[i][j]+f[i+1][k]*f[i+1+k][j-1-k])%mod;
                }
            }
        }
        printf("%lld
    ",f[1][n]);
    }
    signed main(){
        int T=read();
        while(T--)work();
    }
    int read(){
        int s=0,f=0;char ch=getchar();
        while(!isdigit(ch))f=ch=='-',ch=getchar();
        while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
        return f?-s:s;
    }
    /*
    g++ 3.cpp 
    ./a.out
    3
    5 0
    3 2
    1 2
    2 3
    3 3
    1 2
    2 3
    3 1
    */
    View Code

     

     

       

       2、树形背包,子树信息合并。

       3、考虑每条边贡献。

     

     

     四、其它一些

      g[i][j][0/1],i:a->len; j:b->len.0:a,1:b;每次递归时重新求。数据小复杂度正确。不必苛求预处理。

      

    #include<bits/stdc++.h>
    #define F(i,a,b) for(int i=a;i<=b;++i)
    #define LL long long
    #define pf(a) printf("%d ",a)
    #define PF(a) printf("%lld ",a)
    #define pdb(a) printf("%.3lf ",a)
    #define phn puts("")
    using namespace std;
    #define int LL 
    #define N 505
    int read();
    const int mod=998244353,inv2=499122177;
    int n,a[N];
    int f[12][N][N],dp[N];//相对位置。
    int g[N][N][2];
    int qpow(int x,int k){int s=1;for(;k;k>>=1,x=x*x%mod)if(k&1)s=s*x%mod;return s;}
    struct nd{
        int w,pos;
        friend int operator <(nd x,nd y){
            return x.w<y.w||(x.w==y.w&&x.pos<y.pos);
        }
    }b[N];
    void solve(int o,int l,int r){
        if(l==r){
            f[o][b[l].pos][1]=1;return ;
        }    
        int mid=l+r>>1;
        solve(o-1,l,mid);solve(o-1,mid+1,r);
        sort(b+l,b+mid+1);sort(b+mid+1,b+r+1);
        int p1=l-1,p2=mid,l1,l2,s1=0,s2=0;
        F(w,1,1000){
            s1=s2=0;
            l1=p1,l2=p2;
            while(p1<mid&&b[p1+1].w==w)++p1,++s1;
            while(p2<r&&b[p2+1].w==w)++p2,++s2;
            if(!s1&&!s2)continue;
            F(i,0,s1)F(j,0,s2)g[i][j][0]=g[i][j][1]=0;
            g[0][0][0]=1;
            F(i,0,s1){
                F(j,0,s2){
                    if(i<s1&&j<s2){
                        (g[i+1][j][0]+=(g[i][j][0]+g[i][j][1])*inv2)%=mod;
                        (g[i][j+1][1]+=(g[i][j][0]+g[i][j][1])*inv2)%=mod;
                    }
                    else if(i<s1)(g[i+1][j][0]+=(g[i][j][0]+g[i][j][1]))%=mod;
                    else if(j<s2)(g[i][j+1][1]+=(g[i][j][0]+g[i][j][1]))%=mod;
                }
            }
     /*       if(o==1){
                PF(l);PF(r);puts("a");
                F(i,0,1){F(j,0,1)pdb(g[i][j][0]);phn;}phn;
                 F(i,0,1){F(j,0,1)pdb(g[i][j][1]);phn;}phn;
            }*/
            F(i,l1+1,p1){
                F(x,1,s1){
                    F(y,0,s2){
                        (f[o][b[i].pos][x+y]+=f[o-1][b[i].pos][x]*g[x][y][0])%=mod;  
                    }
                }
            }
            F(i,l2+1,p2){
                F(y,1,s2){
                    F(x,0,s1){
                        (f[o][b[i].pos][x+y]+=f[o-1][b[i].pos][y]*g[x][y][1])%=mod;
                    }
                }
            }    
        /*    if(o==1&&w==2){
                puts("b");
               pdb(g[0][1][1]);pdb(f[o][2][1]);phn;
            }*/
        }
    }
    signed main(){
        n=read();F(i,1,n)a[i]=read();
        F(i,1,n)b[i]=(nd){a[i],i};
        int ed=0;
        while((1<<ed)<n)++ed;   
        solve(ed,1,n);
        sort(b+1,b+n+1);
        int p=0,s=0,las=0;
    /*    F(o,0,3){
            F(i,1,n){
                F(j,1,n)pdb(f[o][i][j]);phn;} phn;}*/
        F(w,1,1000){
            s=0;
            while(p<n&&b[p+1].w==w)++p,++s;
            if(!s)continue;
            F(i,p-s+1,p){
                F(j,1,s){
                    (dp[b[i].pos]+=f[ed][b[i].pos][j]*(las+j))%=mod;
                }
            }
            las=p;
        }
        F(i,1,n){
            printf("%lld ",dp[i]);
        }phn;
    }
    int read(){
        int s=0,f=0;char ch=getchar();
        while(!isdigit(ch))f=ch=='-',ch=getchar();
        while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
        return f?-s:s;
    }
    /*
    g++ 2.cpp
    ./a.out
    5
    5 2 1 2 2 
    
    */
    测试91T2

     

     

     

     

     

  • 相关阅读:
    怎样提高开发效率
    ASP.NET/C#获取文章中图片的地址
    listBox的用法
    ASP.NET中的asp:label和asp:literal
    ID,ClientID,UniqueID的区别
    asp.net中的属性
    数据库中的值为空的判断 ,并赋初值
    属性器,转换从数据库中读取的状态
    Collections.emptyList() and Collections.EMPTY_LIST
    InnoDB
  • 原文地址:https://www.cnblogs.com/seamtn/p/11593007.html
Copyright © 2011-2022 走看看