zoukankan      html  css  js  c++  java
  • P3352-[ZJOI2016]线段树【dp】

    正题

    题目链接:https://www.luogu.com.cn/problem/P3352


    题目大意

    (n)个数字的一个序列,每次随机选择一个区间让这个区间所有数等于这个区间的最大值,重复(q)次,对每个位置求所有情况下这个位置的值的和。

    (1leq n,qleq 400),保证数据随机


    解题思路

    (f_{k,l,r})表示使用了(k)次目前覆盖了极大区间(l,r)时的方案。

    这个极大区间就是无法继续向左右扩展(就是左右两边是边界或者比这个区间内所有数都大),不然相同的方案会统计入不同的数组导致算重。

    然后每次我们找一个数字开始向左右扩展到极大区间进行(dp),然后(dp)方程是

    [f_{k,l,r}=f_{k-1,l,r} imes g_{l,r}+sum_{i=L}^{l-1}f_{k-1,i,r}+sum_{i=r+1}^{R}f_{k-1,l,i+1} ]

    也就是固定端点的情况下扩展极大区间,因为是反过来的所以这样是对的。

    然后记录一个(dp)数组(ans_{i,j})表示数字(i)至少为第(j)小的情况数,这个每次(dp)后都可以统计。

    上面每个(dp)区间相当于笛卡尔树上的区间,因为数据随机,所以每个位置只会计算(log)次。

    时间复杂度(O(nq^2+n^3))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=410,P=1e9+7;
    ll n,q,a[N],b[N],rk[N],f[2][N][N],ans[N][N],cnt[N];
    void solve(ll x,ll L,ll R){
    	for(ll i=L;i<=R;i++)
    		for(ll j=i;j<=R;j++)
    			f[0][i][j]=f[1][i][j]=0;
    	f[0][L][R]=1;
    	for(ll k=1;k<=q;k++){
    		for(ll i=L;i<=R;i++)
    			for(ll j=i;j<=R;j++)
    				f[k&1][i][j]=f[~k&1][i][j]*(cnt[j-i+1]+cnt[i-1]+cnt[n-j]);
    		for(ll i=L;i<=R;i++){
    			ll buf=0;
    			for(ll j=R;j>=i;j--){
    				(f[k&1][i][j]+=buf)%=P;
    				(buf+=f[~k&1][i][j]*(n-j))%=P;
    			}
    		}
    		for(ll j=L;j<=R;j++){
    			ll buf=0;
    			for(ll i=L;i<=j;i++){
    				(f[k&1][i][j]+=buf)%=P;
    				(buf+=f[~k&1][i][j]*(i-1))%=P;
    			}
    		}
    	}
    	for(ll i=L;i<=R;i++){
    		ll buf=0;
    		for(ll j=R;j>=i;j--){
    			(buf+=f[q&1][i][j])%=P;
    			(ans[j][rk[x]]+=buf)%=P;
    		}
    	}
    	return;
    }
    signed main()
    {
    	scanf("%lld%lld",&n,&q);
    	for(ll i=1;i<=n;i++)cnt[i]=i*(i+1)/2; 
    	for(ll i=1;i<=n;i++){
    		scanf("%lld",&a[i]);
    		b[i]=a[i];
    	}
    	sort(b+1,b+1+n);
    	ll m=unique(b+1,b+1+n)-b-1;
    	for(ll i=1;i<=n;i++)rk[i]=lower_bound(b+1,b+1+m,a[i])-b;
    	for(ll i=1;i<=n;i++){
    		ll L=i,R=i;
    		while(L>1&&a[L-1]<a[i])L--;
    		while(R<n&&a[R+1]<a[i])R++;
    		solve(i,L,R);
    	}
    	for(ll i=1;i<=n;i++){
    		ll sum=0;
    		for(ll j=1;j<=n;j++){
    			if(!ans[i][j]){continue;}
    			for(ll k=1;k<j;k++)
    				(ans[i][j]+=P-ans[i][k])%=P;
    			(sum+=ans[i][j]*b[j]%P)%=P;
    		}
    		printf("%lld ",sum);
    	}
    	return 0;
    }
    
  • 相关阅读:
    MVB设备分类
    MVB帧
    也说析构---C++
    oracle中以dba_、user_、v$_、all_、session_、index_开头
    查看Oracle的表中有哪些索引(用user_indexes和user_ind_columns)
    Spark_总结五
    Spring编程式和声明式事务实例讲解
    缓存穿透,缓存击穿,缓存雪崩解决方案分析
    redis持久化2
    redis的持久化方式
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15155897.html
Copyright © 2011-2022 走看看