• 紫书 习题 10-21 UVa 1649 (组合数)


    C(n, k) = m, 固定k,枚举k

    这里用到了组合数的一个性质

    当k固定的时候,C(2 * k, k) 最小 

    C(m, k)最大(对于这道题而言是这样,因为大于m 就最终答案不可能为m了)

    所以就二分去枚举2*k到m之间了。

    最后注意算组合数的时候超过m可以直接返回,同时比较时候可能会超出long long

    有小技巧可以避免,看代码。

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #define REP(i, a, b) for(int i = (a); i < (b); i++)
    using namespace std;
    
    typedef long long ll;
    ll m;
    vector<pair<ll, ll> > g;
    
    ll c(ll n, int k)
    {
    	ll ret = 1;
    	REP(i, 1, k + 1)
    	{
    		if ((ret / i) > (m / (n - i + 1))) return m + 1; //注意这里可能会爆long long,所以要换一种写法 
    		ret = ret * (n - i + 1) / i;
    	}
    	return ret;
    }
    
    void solve()
    {
    	for(int k = 1; c(2 * k, k) <= m; k++)
    	{
    		ll l = 2 * k - 1, r = m + 1;
    		while(l + 1 < r)
    		{
    			ll mid = (l + r) >> 1;
    			ll t = c(mid, k);
    			if(t == m)
    			{
    				g.push_back(make_pair(mid, k));
    				if(mid != k * 2)
    					g.push_back(make_pair(mid, mid - k));
    				break;
    			}
    			else if(t < m) l = mid;
    			else r = mid;
    		}
    	}
    }
    
    int main()
    {
    	int T;
    	scanf("%d", &T);
    	
    	while(T--)
    	{
    		g.clear();
    		scanf("%lld", &m);
    		
    		solve();
    		sort(g.begin(), g.end());	
    				
    		printf("%d
    (%lld,%lld)", g.size(), g[0].first, g[0].second);
    		REP(i, 1, g.size()) printf(" (%lld,%lld)", g[i].first, g[i].second);
    		puts("");			
    	}
    	
    	return 0;
    }
  • 相关阅读:
    弦图点染色问题
    BZOJ1098: [POI2007]办公楼biu
    BZOJ1097: [POI2007]旅游景点atr
    BZOJ1068: [SCOI2007]压缩
    BZOJ1055: [HAOI2008]玩具取名
    BZOJ4199: [Noi2015]品酒大会
    BZOJ2527: [Poi2011]Meteors
    BZOJ1493 [NOI2007]项链工厂
    BZOJ1095 ZJOI2007 Hide 捉迷藏
    bzoj1468 Tree
  • 原文地址:https://www.cnblogs.com/sugewud/p/9819468.html
Copyright © 2020-2023  润新知