【BZOJ 4574】【ZJOI 2016】线段树

公式来自:
http://www.cnblogs.com/Dragon-Light/p/6475923.html
题解(也许)有点错误,加以修改

枚举每一个数,设f[k][i][j]表示k轮之后i到j区间内的数都小于等于枚举的那个数的方案数。注意这个[i,j]的范围是严格的,也就是说第i-1和j+1个数都是大于的。(换句话说,都是顶着上限的)
显然这个数所能影响的范围是从左边第一个大于它的数开始到右边第一个大于它的数结束。设这个最大的范围是[l,r]。
f[k][i][j]=i1u=l(f[k1][u][j](u1))+rv=j+1(f[k1][i][v](nv))+f[k1][i][j](sum(i1)+sum(ji+1)+sum(nj))
其中:sum(x)表示区间大小为x的子区间数量,即x*(x+1)/2。

——————————————————————
1 - - - - - l - - - - - -u - - - - -i - - - - - - -j - - - - - - -n
解释一下这个公式。第一种情况表示从[u,j]这个区间缩减为[i,j]这个区间,说明[u,i-1]这个区间的数变大了。确定i-1为右端点,[1,u-1]中任取一个左端点,都能够使[u,i-1]这些数变大(因为根据条件,第u-1个数一定是大于中间这些数的)。
第二种情况同理。
第三种情况就是区间全部在i左侧,全部在j右侧,和在i、j中间,这三种情况是没有影响的。

直接计算的时间复杂度是O(q*n^4),其实真正做的时候区间不可能都是满的,所以其实完全不到这个复杂度,这样做已经可以拿到50分了,当时考场也有人做到这个分数,应该就是这么做的。
下面考虑一个优化,第一种情况和第二种情况显然是可以用前缀和计算出的,这样时间复杂度降到O(q*n^3),这个复杂度同样也是高估的(其实就算是满的3s应该也能卡过去),这样做就是70分,同样也有人做到这个分数,应该就是用了前缀和。前缀和看起来很容易想到,但是本题的公式本来就复杂,在考场上如果不能够清晰地列出公式,不太会往这方面考虑。
然后这个复杂度在UOJ上面竟然是跑得过去的,基本都是2.6s卡过去。。然而本机一测,10s+。。。%UOJ的氧气开关。
你问我考场上的正解?大概就是卡常数吧,少用一些取模运算就好啦。

#include
#include
#include
#include 
#include
#include
#include
#include
#include
#define ll long long
#define inf 1000000000
#define mod 1000000007
#define N 405
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
ll a[N],b[N],f[3][N][N],d[N],sum[N][N],rk[N];
int n,q,i,j,k,l,r;
ll res;
bool cmp(const int &x,const int &y) {return a[x]<a[y];}
int c(int x) {return x*(x+1)>>1;}
void check(int l,int r,int x)
{
    int i,j,k;
    //memset(f,0,sizeof(f));
    fo(i,l,r) fo(j,i,r) f[0][i][j] = 0; 
    f[0][l][r] = 1;
    fo(k,1,q)
        {
            fo(i,l,r) d[i] = 0;
            fo(i,l,r)
                {
                    fo(j,i,r)
                        {
                            f[k&1][i][j] =  d[j] % mod;
                            d[j] += (f[(k-1)&1][i][j] * (i - 1)) % mod;
                        }
                }

            fo(i,l,r)
                {
                    ll t = 0;
                    fd(j,r,i)
                        {
                            f[k&1][i][j] = (f[k&1][i][j] + t) % mod; 
                            t = (t + f[(k-1)&1][i][j] * (n - j)) % mod;
                        }
                }

            fo(i,l,r) d[i] = 0;
            fo(i,l,r)
                {
                    fo(j,i,r) f[k&1][i][j] =  (f[k&1][i][j] + d[j]) % mod;
                    d[j] += (f[(k-1)&1][i][j] * (i - 1)) % mod;
                }

            fo(i,l,r)
                fo(j,i,r)
                    f[k&1][i][j] = (f[k&1][i][j] + f[(k-1)&1][i][j] * (c(i-1) + c(j-i+1) + c(n-j))) % mod;
        }   
    fo(i,l,r)
        {
            ll t = 0;
            fd(j,r,i)
                {
                    t = (t + f[q&1][i][j]) % mod;
                    sum[j][rk[x]] = (sum[j][rk[x]] + t) % mod;  
                }
        }

}
int main()
{
    scanf("%d%d",&n,&q);
    fo(i,1,n) scanf("%d",&a[i]); fo(i,1,n) b[i] = i; sort(b+1,b+n+1,cmp);
    fo(i,1,n) rk[b[i]] = i;
    fo(i,1,n)
        {
            l = i; while (a[l-1] <= a[i] && l > 1) l--;
            r = i; while (a[r+1] <= a[i] && r < n) r++;
            check(l,r,i);
        }
    fo(i,1,n)
        {
            res = 0;
            fo(j,1,n)
                {
                    if (!sum[i][j]) continue;
                    fo(k,1,j-1) sum[i][j] -= sum[i][k];
                    while (sum[i][j] < 0) sum[i][j] += mod;
                    res = (res + a[b[j]] * sum[i][j])%mod; 
                    res %= mod;
                }
            while (res < 0) res += mod; res %= mod; printf("%lld",res);
            if (i != n) printf(" "); else printf("\n");
        }
    return 0;
}

你可能感兴趣的:(dp)