zoukankan      html  css  js  c++  java
  • bzoj 4574: [Zjoi2016]线段树

    Description

    小Yuuka遇到了一个题目:有一个序列a_1,a_2,?,a_n,q次操作,每次把一个区间内的数改成区间内的最大值,问
    最后每个数是多少。小Yuuka很快地就使用了线段树解决了这个问题。于是充满智慧的小Yuuka想,如果操作是随机
    的,即在这q次操作中每次等概率随机地选择一个区间l,r,然后将这个区间内的数改成区间内最大
    值(注意这样的区间共有(n(n+1))/2个),最后每个数的期望大小是多少呢?小Yuuka非常热爱随机,所以她给出
    的输入序列也是随机的(随机方式见数据规模和约定)。对于每个数,输出它的期望乘((n(n+1))/2)^q再对10^9+7取模的值。

    Solution

    考虑每一个位置最后会变成的值,这个不好算
    考虑每一个位置作为最大值可以影响到的范围
    枚举每一个点作为最大值 (x), (DP) 算贡献
    (f[k][i][j]) 表示前 (k) 次操作, ([i,j]) 的最大值小于等于 (x) 的方案数
    (i-1),(j+1) 比区间最大值大的,每次选的区间包含这两个位置就会缩短区间,否则不变
    分区间长度缩短和不变两种写就好了
    最后求出的是小于等于 (x) 的方案数,容斥一下得到等于 (x) 的方案数
    随机数据下,复杂度期望 (O(q*n^2))

    #include<bits/stdc++.h>
    #define RG register
    using namespace std;
    const int N=405,mod=1e9+7;
    int n,m,a[N],g[N],b[N],f[2][N][N],s[N][N];
    inline void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
    inline void solve(int l,int r,int p){
    	for(int i=l;i<=r;i++)
    		for(int j=l;j<=r;j++)f[0][i][j]=f[1][i][j]=0;
    	bool t=0;
    	f[0][l][r]=1;
    	for(int P=1;P<=m;P++,t^=1){
    		for(int i=l;i<=r;i++){
    			int sum=0;
    			for(RG int j=r;j>=i;j--){
    				int w=f[t][i][j];
    				add(f[t^1][i][j],sum);
    				sum=(sum+1ll*w*(n-j))%mod;
    			}
    		}
    		for(int j=l;j<=r;j++){
    			int sum=0;
    			for(RG int i=l;i<=j;i++){
    				int w=f[t][i][j];f[t][i][j]=0;
    				add(f[t^1][i][j],sum);
    				add(f[t^1][i][j],1ll*w*(1ll*g[i-1]+g[j-i+1]+g[n-j])%mod);
    				sum=(sum+1ll*w*(i-1))%mod;
    			}
    		}
    	}
    	for(int i=l;i<=r;i++)
    		for(int j=i;j<=r;j++)
    			if(f[t][i][j])
    				for(RG int k=i;k<=j;k++)s[k][a[p]]=(s[k][a[p]]+f[t][i][j])%mod;
    }
    int main(){
    	freopen("pp.in","r",stdin);
    	freopen("pp.out","w",stdout);
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]),g[i]=i*(i+1)>>1,b[i]=a[i];
    	sort(b+1,b+n+1);
    	int tp=unique(b+1,b+n+1)-b-1;
    	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+tp+1,a[i])-b;
    	for(int i=1;i<=n;i++){
    		int l=i,r=i;
    		while(l>1 && a[l-1]<=a[i])l--;
    		while(r<n && a[r+1]<=a[i])r++;
    		solve(l,r,i);
    	}
    	for(int i=1;i<=n;i++){
    		int ans=0;
    		for(int j=1;j<=tp;j++)
    			if(s[i][j]){
    				for(int k=1;k<j;k++)s[i][j]=(s[i][j]-s[i][k]+mod)%mod;
    				ans=(ans+1ll*b[j]*s[i][j])%mod;
    			}
    		printf("%d ",ans);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    c# 调用C++动态库 问题
    Web应用简易框架。
    【转】SVN历史版本删除(为SVN库瘦身)
    程序员7武器序
    小系统单据自动生成存储过程
    【转】数据库和数据仓库的区别
    jQuery之extend 函数
    .NET单元测试断言(Assert)
    c#类型转换操作符:as和is
    oracle 表数据合并
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8991291.html
Copyright © 2011-2022 走看看