zoukankan      html  css  js  c++  java
  • 联考20200727 T1 小$omega$数排列


    分析:
    遇到这种恶心绝对值加排列的问题,大概会往重新排序从小到大加入的方向去思考
    先把值从小到大排序,一个一个加入
    假设现在已经加入的值构成一些连续段,现在分类讨论:
    加入一个数形成新的一段,那么它对答案的贡献系数为(-2)
    在一段的左右加一个数,它对答案不会有贡献
    加入一个数让两段连在一起,那么它对答案的贡献系数为(2)
    进行DP,设(f_{i,j,k})表示放了(i)个数形成(j)段,贡献为(k)的方案数
    发现边界情况需要单独考虑,于是加一维(t=0/1/2)
    (f_{i,j,k,t})表示放了(i)个数形成(j)段,贡献为(k),边界填了(0/1/2)的方案数
    五种情况讨论:
    1、加一个数形成新的一段
    2、在一段左右加一个数
    3、将两个段连起来
    4、边界填一个数形成新的一段
    5、在一段左右加一个数并且该数在边界上

    现在我们发现(k)的范围很让人不爽,可能会出现负数,规模难以估计
    我们在加入某一个数后强行让(k'=k+(2j-t)a_i),后面这个是要让所有段连通至少需要的代价,并且(k'geq 0)
    如果(k')这个至少得贡献都大于了(L)就没有必要继续DP了
    相当于利用了放缩的思想
    复杂度(O(3n^2L))

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<iostream>
    #include<map>
    #include<string>
    
    #define maxn 105
    #define MOD 1000000007
    
    using namespace std;
    
    inline int getint()
    {
    	int num=0,flag=1;char c;
    	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
    	return num*flag;
    }
    
    int n,L;
    int a[maxn];
    int f[maxn][maxn][10*maxn][3];
    
    inline int upd(int x){return x<MOD?x:x-MOD;}
    inline void getf(int i,int j,int k,int t,int num)
    {
    	k=k+(2*j-t)*a[i];
    	if(k<=L)f[i][j][k][t]=upd(f[i][j][k][t]+num);
    }
    
    int main()
    {
    	n=getint(),L=getint();
    	for(int i=1;i<=n;i++)a[i]=getint();
    	sort(a+1,a+n+1);
    	if(n==1){printf("1
    ");return 0;}
    	f[0][0][0][0]=1;
    	for(int i=0;i<n;i++)for(int j=0;j<=i;j++)for(int k=0;k<=L;k++)for(int t=0;t<=2;t++)
    	{
    		int tmp=f[i][j][k][t];if(!tmp)continue;
    		int val=k-(2*j-t)*a[i];
    		getf(i+1,j+1,val-2*a[i+1],t,1ll*tmp*(j+1-t)%MOD);
    		if(j)getf(i+1,j,val,t,1ll*tmp*(2*j-t)%MOD);
    		if(j>=2)getf(i+1,j-1,val+2*a[i+1],t,1ll*tmp*(j-1)%MOD);
    		if(t<2)
    		{
    			getf(i+1,j+1,val-a[i+1],t+1,1ll*tmp*(2-t)%MOD);
    			if(j)getf(i+1,j,val+a[i+1],t+1,1ll*tmp*(2-t)%MOD);
    		}
    	}
    	int ans=0;
    	for(int i=0;i<=L;i++)ans=upd(ans+f[n][1][i][2]);
    	printf("%d
    ",ans);
    }
    

  • 相关阅读:
    利用事件委托实现用户控件中的行为触发所在页面的处理函数
    mootools系列:打造属于你自己的Popup(弹出框)——基本结构篇
    mootools系列:打造属于你自己的Popup(弹出框)——外观及应用篇
    所见即所得的Excel报表生成(二)——从Html table到Excel Cell
    Excel操作服务器端配置
    顺丰单号生成规则
    代理模式Proxy
    解释器模式Interpreter
    线性表
    总有一款合适的框架
  • 原文地址:https://www.cnblogs.com/Darknesses/p/13390239.html
Copyright © 2011-2022 走看看