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\n",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\n",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\n",ans);
    else if(k==1)printf("%.1lf\n",ans);
    else if(k==2)printf("%.2lf\n",ans);
    else if(k==3)printf("%.3lf\n",ans);
    else if(k==4)printf("%.4lf\n",ans);
    else if(k==5)printf("%.5lf\n",ans);
    else if(k==6)printf("%.6lf\n",ans);
    else if(k==7)printf("%.7lf\n",ans);
    else if(k==8)printf("%.8lf\n",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\n",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\n",f[i][0],f[i][1]);
    }
    ans=(ans-n+mod)%mod;
    printf("%d\n",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\n",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\n",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("\n")
#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("\n")
#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\n",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\n",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\n",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\n",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\n",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\n",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\n",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\n",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

 

 

 

 

 

 

posted @ 2019-09-26 17:10  seamtn  阅读(418)  评论(0编辑  收藏  举报