zoukankan      html  css  js  c++  java
  • ICPC Shanghai 网络赛 Stone Game

    题意

    给定一个可重集(S),请求出满足下面条件的子集(S')个数:

    • (S'geq S-S')
    • (S'-a_i leq S-S'),其中(a_i in S')

    (|S|leq 300, a_i leq 500)


    解法

    ( t {hin})有味的计数题,虽然(A)的人很多,但是我没想出来

    我们令满足条件的集合为集合(A),其补集为(B)

    首先要发现一个性质,如果集合(A)中的元素最小值为(a),那么如果(A)为满足条件的子集,(A-aleq B)

    那么我们考虑枚举(a)

    为了保证单调,我们把所有元素从小到大排序,倒序枚举(a)

    我们假设当前枚举到的(a)(a_i),那么(a_1)(a_{i-1})一定都属于(B)集合

    这是因为我们枚举的(a)是集合中的元素最小值,所以小于(a)的元素一定不在(A)集合中

    此时合法的(A)集合中包含的元素一定有(a_i),并且剩下的元素都来自(a_{i+1})(a_n)

    我们先处理出一个(DP)数组(f[i][j])代表考虑(i)(n)中的元素,选择的(A)集合与(B)集合权值之差为(j)的方案数

    转移方程还是比较好想的,即

    [f[i][j]=f[i+1][j+a_i]+f[i+1][j-a_i] ]

    至于下标为负的情况,强制转正即可

    由于(a_1)(a_{i-1})一定属于(B)集合,我们统计一下前缀和,那么(B)集合的权值至少为(sum_{i-1})

    为了满足上面的条件即$A-aleq B $

    我们把(A)分为两个部分(A=a+A'),其中(A')(A)集合在(i)右侧的部分,同样的,把(B)也分为(i)左侧的部分(B'')(很明显(B'')的权值大小即(sum_{i-1}))与右侧的部分(B')

    [ecause A-a leq B \ herefore a+A'-a leq B'+B'' \ herefore A'-B' leq sum_{i-1} ]

    又要满足限制(1),即$Ageq B $

    [ecause Ageq B \ herefore a+A' geq B'+B''\ herefore A'-B' geq sum_{j-1}-a ]

    这样我们就确定了上下界,枚举差值转移即可


    代码

    蒯了( t {JR})的代码(考场上就(A)了,是真的强)

    /*******************************
    Author:Morning_Glory
    LANG:C++
    Created Time:2019年09月15日 星期日 12时55分49秒
    *******************************/
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int maxn = 305;
    const int maxm = 300005;
    const int mid = 150000;
    const int mod = 1000000007;
    //{{{cin
    struct IO{
    	template<typename T>
    	IO & operator>>(T&res){
    		res=0;
    		bool flag=false;
    		char ch;
    		while((ch=getchar())>'9'||ch<'0')	flag|=ch=='-';
    		while(ch>='0'&&ch<='9')	res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
    		if (flag)	res=~res+1;
    		return *this;
    	}
    }cin;
    //}}}
    int T,n,mi,mx,ans;
    int a[maxn],sum[maxn];
    int f[2][maxm];
    bool now,pre;
    void add (int &x,int y){	x=((x+y)%mod+mod)%mod;}
    //{{{solve
    int solve (int x)
    {
    	//x左边所有的数都不可以被选
    	int c=sum[x-1]+mid;
    	int res=0;
    	for (int i=0;i<=a[x];++i)	add(res,f[pre][c-i]);
    	return res;
    }
    //}}}
    int main()
    {
    	cin>>T;
    	while (T--){
    		cin>>n;
    		now=pre=false;
    		ans=mx=0;
    		memset(f,0,sizeof(f));
    		f[0][mid]=1;
    		for (int i=1;i<=n;++i)	cin>>a[i],mx+=a[i];
    		mi=mid-mx,mx+=mid;
    		sort(a+1,a+n+1);
    		for (int i=1;i<=n;++i)	sum[i]=sum[i-1]+a[i];
    
    		for (int i=n;i>=1;--i){
    			now=!now,pre=!now;
    			for (int j=mi;j<=mx;++j)
                    f[now][j]=(f[pre][j-a[i]]+f[pre][j+a[i]])%mod;
    			add(ans,solve(i));
    		}
    
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    mysql 重置root 账户密码
    Applicationpoolidentity 好有趣哦
    类模板的困扰 LNK2019 (转)
    C++中定义比较函数的三种方法
    Spring的AOP,Struts2的拦截器(Interceptor),以及springMVC的(interceptor)
    MyBatis与Hibernate总结篇
    Java中的回调
    闲来重写一下快速排序
    【lucene】一个简单的招聘网站的建立
    【Lucene】小谈lucene的BooleanQuery查询对象
  • 原文地址:https://www.cnblogs.com/VeniVidiVici/p/11523815.html
Copyright © 2011-2022 走看看