bzoj 4574: [Zjoi2016]线段树 动态规划

       题意就是求每个数在所有方案中的最终值的和。显然一个数经过若干次变化一定会变成另外一个数,那么离散化后,令g[i][j]表示i这个数最终变成从小到大第j个的方案数。一个直观的思路是,我们枚举j,那么显然g[i][j]>0的i的范围是(l,r),其中a[l]和a[r]是第j大的数两侧分别第一个大于这个从小到大第j个数的数(由于是随机因此可以假定没有两个数相同)。此时,

       如果令f[k][x][y]表示经过k轮后,恰好是[x,y]这个范围内的数都变成了从小到大第j个数的方案数。但是这样会存在问题,就是如果某一轮的操作跨过了l或r,就会造成[l,r]中某一些数>从小到大第j个数,这样再转移就会出错。

       那么(根据jry老司机的博客)令f[k][x][y]表示经过k轮后,恰好[x,y]这个范围内的数都<=从小到大第j数的方案数,这样就可以转移了。显然f[k][x][y]必然由f[k][u(u<x)][y]和f[k][x][v(v>y)],然后操作任意[t(t<l),u]和[v,t(t>r)]得到;或者直接由f[k][x][y],然后任意一个操作[u,v],其中[u,v]∩[x,y]=0得到。

       然后重新领g[i][j]表示第i个数最终<=从小到大第j个数的方案数,然后减一减即可。

       后来有一个卡常的技巧是每一轮都用long long暴力加起来,这一轮结束的时候再取模。这样瞬间应该可以快很多。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 1000000007
#define ll long long
#define N 405
#define calc(x) ((x)*((x)+1)>>1)
using namespace std;

int n,m,f[2][N][N],g[N][N],s[N][N],a[N],b[N],c[N],rnk[N];
bool cmp(const int &x,const int &y){ return a[x]<a[y]; }
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void solve(int l,int r,int p){
	int i,j,k,last,now=0,tmp;
	for (i=l; i<=r; i++) for (j=i; j<=r; j++) f[0][i][j]=0;
	f[0][l][r]=1;
	for (k=1; k<=m; k++){
		last=now; now^=1;
		for (i=l; i<=r; i++){
			tmp=0;
			for (j=r; j>=i; j--){
				f[now][i][j]=tmp; tmp=(tmp+(ll)f[last][i][j]*(n-j))%mod;
			}
		}
		for (i=l; i<=r; i++) c[i]=0;
		for (i=l; i<=r; i++)
			for (j=i; j<=r; j++){
				f[now][i][j]=(f[now][i][j]+c[j])%mod; c[j]=(c[j]+(ll)f[last][i][j]*(i-1))%mod;
			}
		for (i=l; i<=r; i++)
			for (j=i; j<=r; j++) f[now][i][j]=(f[now][i][j]+(ll)f[last][i][j]*s[i][j])%mod;
	}
	for (i=l; i<=r; i++){
		tmp=0;
		for (j=r; j>=i; j--){
			tmp=(tmp+f[now][i][j])%mod; g[j][rnk[p]]=(g[j][rnk[p]]+tmp)%mod;
		}
	}
}
int main(){
	n=read(); m=read(); int i,j,l,r,tmp,ans;
	for (i=1; i<=n; i++){ a[i]=read(); b[i]=i; }
	sort(b+1,b+n+1,cmp);
	for (i=1; i<=n; i++) rnk[b[i]]=i;
	for (i=1; i<=n; i++)
		for (j=i; j<=n; j++) s[i][j]=calc(i-1)+calc(n-j)+calc(j-i+1);
	for (i=1; i<=n; i++){
		l=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 (i=1; i<=n; i++){
		tmp=ans=0;
		for (j=1; j<=n; j++) if (g[i][j]){
			g[i][j]-=tmp; if (g[i][j]<0) g[i][j]+=mod;
			ans=(ans+(ll)g[i][j]*a[b[j]])%mod; tmp=(tmp+g[i][j])%mod;
		}
		printf("%d%c",ans,(i<n)?' ':'\n');
	}
	return 0;
}

by lych

2016.5.3

    原文作者:动态规划
    原文地址: https://blog.csdn.net/lych_cys/article/details/51306125
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞